Resources

Getting Started with LLM APIs in R

Getting Started with LLM APIs in R - Sara Altman Abstract: LLMs are transforming how we write code, build tools, and analyze data, but getting started with directly working with LLM APIs can feel daunting. This workshop will introduce participants to programming with LLM APIs in R using ellmer, an open-source package that makes it easy to work with LLMs from R. We’ll cover the basics of calling LLMs from R, as well as system prompt design, tool calling, and building basic chatbots. No AI or machine learning background is required—just basic R familiarity. Participants will leave with example scripts they can adapt to their own projects. Resources mentioned in the workshop: * Workshop site: https://skaltman.github.io/r-pharma-llm/ * ellmer documentation: https://ellmer.tidyverse.org/ * shinychat documentation: https://posit-dev.github.io/shinychat/

Feb 8, 2026
1h 37min

image: thumbnail.jpg

Transcript#

This transcript was generated automatically and may contain errors.

So yeah, hi, I'm Sarah. I'll introduce myself in a little bit, but I have some setup instructions here, but I'm going to move past this and I'm going to give you time to get set up in about five minutes, so don't worry about this.

Okay, but yeah, hi, I'm Sarah. Like Eric said, I work at Posit. I'm a senior developer advocate on the AI core team, so I do things like this. I teach workshops, I write blog posts, I do other things. Yeah, it's nice to not really meet all of you, but be talking to all of you. So this workshop is going to be an intro to working with LLM APIs from R, so you don't need to really bring any knowledge about how LLMs work, or machine learning knowledge, or anything like that. As long as you can write code in R and know how to write an R function, this workshop is for you. We're going to take it sort of from the ground up and learn about how LLMs work at a really basic level, how to work with LLM APIs from R, how to make basic shiny apps that use LLMs, and some other things.

So this is really intended as sort of an introduction to these tools. Everything that you should need, or all the information, lives on this website. I think that link is in the chat. So here, there's all the slides and information about what we're going to cover today is on this site. So if you want to follow along, you can take a look here.

So the workshop format, I'm going to talk, I'm going to lecture, there's going to be slides like I'm doing right now. I'm also going to have demos where I'll show you a little bit of code. I'll run an app or something, and then we'll talk about how it works. And then there will also be a series of interactive exercises that you will do on PositCloud. Like I said, I'll give you a minute to get set up in a second. But everything you'll need today is on PositCloud, so you shouldn't have to install any packages or set anything up. Everything's done for you. We have all the packages, all the files, and the API keys up for you on PositCloud.

And like Eric said, if you have questions at any point, you can put them in the chat. I can't promise that I will see them while I'm talking, but I will try to monitor during the exercise portions, and then I'll consolidate questions to answer when we regroup.

Okay, so here's a rough schedule of what we're going to be covering. I'm not going to promise I'm going to stick to these timestamps exactly, but we'll do a little bit of intro, then I'll talk about how conversations with LLMs work, talk more about programming with the APIs, then we'll talk about prompt engineering, and finally we'll talk about tool calling, which is the process of hooking LLMs up to external tools to give them new abilities.

Getting set up on PositCloud

Okay, so in a minute you'll see the PositCloud project. There's a bunch of files in there because it is sort of the whole ecosystem for this workshop, but the things you just need to look at are the exercises and the solutions if you want to see the solutions to the exercises. There's also a demos folder which has the code for all the demos that I'll do today, and you can basically ignore everything else.

Okay, so let's get started. So the first thing that you need to use an AI API is an API key, which might make sense. And just quickly, if you want to do the kinds of things we're going to do in this workshop afterwards, you will need your own API key. So generally how you do this is you sign up for an account with OpenAI or Anthropic, wherever you want to get your model from. You add some payment method, and then they'll give you an API key, and then you're going to store that in your R environment. This looks really similar to if, you know, you've done basically anything with any kind of API that needed a key, you put that in your R environment, and then you can start using these tools from R. So this is just to give you an idea of what you'll need to do afterwards, but for today we're going to give you API keys, and these are already set up in PositCloud.

You'll use these for all our activities. Feel free to play around with the exercises. You don't have to stick exactly to the code there, but like please be generally considerate, you know, don't start pasting in, you know, books or something for the LLM to consider. And then we're going to turn these keys off at the end of the workshop, so if you want to rerun the code later, you will need your own API keys.

Okay, so now I'm going to give you a little bit of time to get set up. So hopefully this link is everywhere to join our workspace. If you don't already have a PositCloud account, you will need to sign up for one and then join the workspace and then go to our RFARMA LLM project. It's possible that when you go to this bit.ly link, it'll take you directly to the RFARMA LLM project. It seems to differ depending on if you already have an account. But at the end of it, you should see an RStudio project open with a bunch of files, and then you're going to run the code in exercises01.hello.llm. I think the actual file is 01.hello.llm.r. You don't do any code, just run it. This is just to check that everything is working, and I will monitor the chat to make sure everyone can get set up.

Okay, so I'm going to start this. There's a timer up here. I'm going to give you six minutes, so go ahead and get started. Yeah, just a friendly reminder for those that joined a few minutes late. I'm going to put the – recopy the links that Sarah provided and put them in the chat again so you can get access just like on the slide there. One thing to note is when you join the PositCloud, you will – after you sign up for that space, you'll see a kind of a welcome page message in PositCloud about getting started with LLM APIs in R. Make sure to click the content tab if you see that page, and then you'll get to the actual workshop PositCloud-like space, so to speak. So, sometimes I can trip people up if they're not used to PositCloud, but I'm doing it on my system right now, and everything seems working.

And yes, to Ivo's comment there, there can be a bit of a load time to deploy the project depending on bandwidth and also the structure of the project itself, so it's pretty common for it to take a minute, a couple minutes. That's why Sarah is very correctly budgeting for this time just in case. Yeah, if it's also loaded by the end of this time, yeah, post in the chat, and we'll try to get it figured out because it shouldn't take, you know, too, too long.

Yeah, it took about a minute for me in how it seems to be. Yep, I'm C, Posit, Workbench, all set to go. Great. And Sarah, from your experience, is there like any particular web browsers that are better for PositCloud than others, or do they all seem to work? I don't know. Yeah, I always use Chrome, but yeah. Typically, the Chrome-based browsers seem to play a bit nicer than sometimes Firefox, but I'm on Firefox right now, and it seems to work. If you have multiple browsers and you have any issues, you might try a different one.

And I assume, Sarah, that once they're into the interface, do they have to run any commands to restore like the package environment or anything, or is it all just work? All the packages should be installed. Like each file will load the packages that that file needs. So the packages won't be loaded, but they're installed. If you can run this exercise 01-hello-llm.r file and it works and you see some kind of result in the console, everything's working and you're good. If that file produces an error, please let me know in the chat.

Great, I'll keep an eye on that as well. So far, so good on my end. Okay, great. Yeah, and this is just to confirm that the API keys were working, you have set up okay, the packages are installed correctly, everything like that.

Okay, great. Glad it seems to be working for at least most people, hopefully everyone. Just keep, if you're still having problems, just keep posting in the chat and we'll try to figure it out. We want everyone to be able to run the code. Okay, great. So again, yeah, that was just to check that everything's working. You didn't have to write any code. So now we can move on.

How conversations with LLMs work

Great, so now we're going to talk about how conversations with LLMs work. So they're at a high level at first and then you'll see some code. So you probably talked to an LLM before, maybe it was ChatGPT or Gemini or something like that, but what's actually going on when you talk?

So you might have done something like this where you ask an LLM a question or you tell it to do something and then you get a response back. And you keep asking questions and then ChatGPT or whatever LLM, it keeps responding and it kind of feels like you're having a conversation with the person. So how is this happening? At a general level, this entire conversation is happening by HTTP requests and responses. And this isn't too different from just sort of normally interacting with the internet. HTTP requests power the internet at a high level. So when you visit a website, your browser sends an HTTP request to the server that's hosting that website. The server then responds to that request by sending back the HTML of the webpage. And then your browser does a bunch of other stuff to get like images and things, but that's generally what's happening. And it's not really that different with an LLM. So talking to an LLM like ChatGPT also happens via these HTTP requests. You send a message to ChatGPT, you're sending an HTTP request to OpenAI. OpenAI processes that request, it runs the model to get the response, and then it sends that response back to you.

But when you send a message to an LLM, you're not just sending text, you're actually sending text with a specific role attached to it. So there are three roles that messages can have, and understanding these is pretty important to working with LLMs programmatically. So the first is the user role. This is attached to what like you as the person typing is sending. And then there's the assistant role, which is attached to the text that the LLM is sending back to you. This might seem like a small detail, but these roles are kind of fundamental to how you control and guide LLM behavior. So there's three roles. So the third one is the system role or the system prompt. This is kind of your instruction manual for the LLM. It's where you as the developer can guide the model's behavior, tell it what constraints it needs to follow, tell it what kind of format you want to see the responses in, that kind of thing. And we'll talk a lot more about system prompts in a later section.

And this isn't really a thing that you as a user, something like ChatGPT would see. Like you don't have access to the ChatGPT system prompt, you can't see it. It's kind of behind the seams, but it is particularly powerful because it persists across this entire conversation, and it's going to shape how the assistant responds to every user message. Okay. So again, there's three roles, the system prompt, the user, and the assistant.

Programming with ellmer

Okay. So now let's see how you actually do anything with LLMs in R. The package that's going to form the basis of everything we're going to do today is ellmer. So ellmer is an open source R package from Posit that makes it really easy to work with LLMs. It handles all the HTTP request API details for you. So you can focus on building things with LLMs rather than sort of wrangling with these lower level details. So let's see what the code looks like. First, we load ellmer just like you would with any other R package.

And then the next step is to create a chat object. This bit of code pretty much everywhere that we write code today. This is sort of the foundational thing that you're going to do. And this is making a chat object. So this is setting up a connection to the LLM provider API. So in this case, it's OpenAI's API. And this chat object is what we're going to use to send messages and handle the responses from the LLM. Now that we have a chat object, we can talk to the LLM from R. So to do that, we call the chat method on the chat object. It's a little confusing that it's chat chat, but we're just calling a method on this object. And this is sending a message to the LLM. So I've asked it to tell us a fact about sheep. And then if you run this in the console, you'll see a result looks like this. And it's telling us apparently sheep have great memories.

OK. So earlier I mentioned that there's different roles that these text messages can have. So here, you just think about what the user and assistant roles are in this example. The user role is assigned to this tell me a quick fact about sheep because that is what I as the user am sending to the LLM. And then the assistant is sending back the fact about sheep. So that has the assistant role attached to it. One nice thing about ellmer is that you can inspect this chat object and see the full conversation history really easily. So if you run chat, you call the chat object, you're going to see an output that looks like this. So it's giving us the entire conversation history. It's pretty brief conversation history because there's only one back and forth. But you can see that it's flagged what has the user role and what has the assistant role. And then you can also see the number of tokens that were used. We'll talk more about tokens in a bit and how much it costs. So it says it costs zero because the number of tokens used was so small, it was approximately zero dollars and zero cents.

OK. What about the third role, the system prompt? Where is that? We didn't set one up, so the model is just kind of using whatever default prompt that it has. In this case, GPT 4.1, whatever default system prompt it has. But you can control the system prompt with ellmer in that chat function with the system prompt argument. So let's say we always ask, we tell it to always answer in typos. This is the system prompt. This is what is going to inform the LLM's behavior across the entire conversation. And now if we ask it a question, it's going to give us the answer in a haiku. So it is obeying our system prompt. This is kind of a silly example because probably isn't very useful, but you can do a lot with system prompts and we'll talk a lot more about this later. But the ability to shape every response the model gives is very useful. You can exert quite a bit of control over how the model behaves just by writing text for the system prompt.

OK, so now let's take a look at the chat object now that we have a system prompt in place. So now we see that in the chat object, the system prompt is stored here and you see it under system. And then we still have the user and assistant responses.

Exercise: word game

OK. So now we're at a second exercise. This time you are going to write a little bit of code. So if you want to refer back to the slides, I'd recommend opening up the website that we've shared in the chat so you can go back through the slides and look at the code that I shared. OK, so this time you're going to open up 02wordgame.r and your goal is mostly just to play around with the bits of code that I've explained in the past couple slides. So you're going to add a system prompt and then you're going to ask a couple questions. You're going to create a new empty chat and ask a second question again. And if these instructions are a little confusing, the point is for you to kind of investigate what's going on with the LLM and try to figure out how it's working.

OK, so it looks like my timer is off the screen, but I'm going to give you five minutes and I'll let you know when that's up.

OK. I don't know if no one posting in the chat means you didn't get it or you did, but we're going to talk a little bit about it. So just if you had particular questions, please ask them so that we know. And I'm happy to answer any questions.

So we're looking at my local Positron instead of PositCloud, just because this is a little bit easier for me, but the code should be the same. Everything should work the same. So what I had you do was write a system prompt and then chat with the LLM a little bit and then make a new chat object and ask the same question. So I'm just going to talk a little bit about this. So let's run this code first.

Eric, can you can you see this? OK, is it big enough? You may want to zoom in one more notch, but that's great. OK, OK, great, thanks. OK, so we run this. So now we have a system prompt in the chat. So let's just take a look at the object. OK, so we see our system prompt appears here. And now let's ask the LLM a question. Or, yeah, we're asking it to guess the word for a person who lives next door in British English, and we'll see why we did in British English in a second.

And so it gives us back that it's guess's neighbor spelled in the British English way. And that's not very interesting. But what happens if we now ask another question? So what helps the car move smoothly down the road? This is my guess is tires. So a couple things to note about this. The first is that each time it is taking into account our system prompt, it knows that each time we ask a question, send a query. We are still playing this word guessing game that we told it about in the system prompt. And then the second is that even though this in British English thing was not in the system prompt, it still answered this second question in British English. In American English, this is not how you spell tires. So it kind of remembered that, or at least it seems like it's remembering that.

So those are two things to note. And now let's try this second example. So now we're making a new chat object. So previously we were using the same chat object each time and we asked two questions. Now we're making a new chat object and asking what helps the car move smoothly down the road. OK, and we're seeing now we're seeing something that you might expect just from like any LLM. It's giving us so much text. It gave us way more information than we need about tires. And this is because we made a new chat object. This one does not have a system prompt. It doesn't know we're playing a guessing game. And it also does not have this history of us saying, telling it to answer in British English because it is a new object with none of that information.

Demo: ClearBot

So I'm going to briefly show you a demo now of an app that makes this a little bit clearer.

OK, so this is a Python app. You don't even know what the code is doing. Sarah, we can't see the app right now. You can't see it? No.

I'll stop and retry. Sometimes when you share an app versus desktop. How's this? Oh, you should just see my Positron window. Oh, you weren't showing the app itself. No, I haven't. I haven't. Here. Let's got it. OK. Sorry for that. No worries.

OK. Great. So this is the Python app. It's called ClearBot. You don't need to know how it works. The demo code is in your PositCloud project. You won't be able to run it in PositCloud without doing a bunch of setup. But the code is there if you ever want to run a Python Shiny app on your own and play around with it. It is pretty cool. OK. So how this works is it's giving us a window into what's going on with the LLMs just to make it a little bit clearer what's actually happening.

So now let's try our same thing that we've been doing. You're playing a word guessing game. Guess the word. I don't think that was exactly what it was. And now let's ask it in British English. What's the name of the person who lives next door? I did not get my system prompt, but that's OK.

I didn't get the system prompt. We're going to ignore that for now because that's not the most important part about this. Why this is useful is we can see the structure of the request and the response. So when we sent a request, we see, again, the user role and my message. And now we can see the structure of the response as well. So we see it has the assistant role, and then it has all of this context where it didn't play the guessing game correctly, but it still kind of got the right answer. OK, but now let's see what happens when we ask it another question.

Sorry, bad job typing. Again, let's just ignore the guessing game for a second. We're just looking at the structure of these responses. OK, and so the thing I want to point out is that in this request, even though you might think I only sent this second request, what makes cars go, it's getting all of the messages that I have sent previously in this conversation. So we're sending the entire history of the conversation to the LLM each time we make a request.

So we're sending the entire history of the conversation to the LLM each time we make a request.

And this is sort of important to know, and I'll explain a little bit more later, but just know that each time you're sending it, you're sending the whole conversation. And that's how it sees this context, and that's how it sort of remembers that we are speaking in British English. And then you can see the response down here. OK, and ordinarily you might not really need to know how these responses and requests are structured, but it is sort of useful to be able to see it so you can visualize what's happening when you use LLM in R.

LLM memory and statelessness

Great. So we demoed ClearBot. And now we're going to talk a little bit more about how LLMs work. So like I said, you're probably used to chatting back and forth with LLMs. You ask a question or send a request, it gives you a response. You say another thing, it gives you another response. And eventually it kind of feels like you're having a normal conversation.

But as ClearBot showed, and as you might have sort of inferred from the exercise, there's something a little bit interesting happening behind the scenes. And I guess it is a conversation, but it might work differently than you might just think if you didn't know how LLMs worked. And so the first thing I want to point out, like I was hinting at, the LLMs, just like the LLM core technology, is stateless. And this means that it kind of forgets everything that happens after each response. All of your requests to the LLM aren't stored somewhere without, you know, like ChatGPT or other tools have sort of other things going on, but the core LLM technology does not have memory in this way. And that's why we're sending the entire history of the conversation each time we make a request. That's how the LLM knows what happened before and how it can sort of give the feeling that it has memory and that it's remembering. You might think of this kind of like talking to someone with amnesia, like maybe they forget what has happened, you know, more than a minute ago. So every time you say something new to them, you have to recap the entire conversation if you want to continue.

This isn't really a limitation of LLMs, it's just how it works. Okay, so yeah, does it remember anything between requests? You have to send the entire conversation history and then it's going to reconstruct the conversation from what you send.

How LLMs generate responses

So now that you understand how conversations work with LLMs, let's briefly talk about how they are actually making these responses. How do they understand what you're asking and how do they construct these responses that feel at least somewhat human-like?

At a high level, LLMs understand things because they're trained on massive amounts of data, essentially everything that is written on the internet and books that they could get their hands on, etc. And by learning patterns from all of this text, they develop the ability to understand or at least seem like they're understanding in context and meaning and how to put together words that feel like natural language to humans. Okay, so once they do all that, they're able to do all these things that we know they can do, like answer questions, write stories, tell jokes, and translate.

Okay, so let's talk a little bit about how LLMs are actually constructing their responses. They don't just generate full sentences at once. What they're doing is generating responses token by token, essentially choosing the most likely next piece based on everything that they've seen so far. This is really core to the idea of LLMs. They think in tokens. This is the fundamental unit they use to construct responses. These tokens might be words, as you'll see in a minute, but they can also be parts of words or individual characters.

So hello is one token, but unconventional happens to be three tokens. And this is important because different models will have different input and output limits given in tokens. API pricing is also usually given by tokens, often in millions of tokens.

And tokens aren't just for words. Images can also be broken into tokens.

Okay, so now I'm going to demo an app that'll give you a better sense for how these tokens work.

Token possibilities demo

Okay, here's my Positron screen again. I'm just going to navigate to demos and then token possibilities. So you have this app as well, and you should be able to run this in your own browser. So I'm just going to go ahead and run this app.

Okay, so let's just go with the prompt that's already there. I'll ask it to write a limerick about a cat. Okay, and so I just want to point out a couple things with this. The first is that you can see the response broken down by tokens. So each bubble is one token. So you'll see sometimes words are tokens, sometimes a comma is a token, some words, yes, some words are multiple tokens like fell. And then the other thing you can do with this app is click on the token and see the different token possibilities.

So let's click on the second word. So we see that once had a 99% probability, but there was other words that it could have been. I guess some of them are also different versions of ones, but there also is was, so it could have been there was. And the LLM at a general level is picking the token that has the highest probability. So it's interesting that it thinks the cat is from Kent. So this one has a little bit more interesting distribution where once had 99% probability, but Kent only had a 25% probability. And the other options I guess are Belize or Peru.

And so at a high level, this is what the LLM is doing. It's going through and picking the most likely tokens, and then it constructs something that gives you a sense that it understands what it is doing and makes sense generally to the human. I don't really know what this limbic means, but generally things they're doing sometimes make sense.

Thinking empirically about LLMs

Okay, great. One thing to know is that what makes LLMs good is that they are not just sort of looking at each token and then thinking what word often comes after the. They are able to look at a higher level at longer range dependencies that traditional models would miss. And that is one of the things that makes them so powerful.

Okay, but after this, we're not really going to talk about how LLMs work. And honestly, if you forget a lot of that, it's okay. To do what we're going to do today, you don't really need to have a detailed understanding of what's going on under the hood. And instead, I would encourage you to think empirically and not theoretically.

What I mean by this is, if you don't know what an LLM is going to do, or if it's going to be good as a task, just try it out and see what happens. We found that it is okay to mostly treat LLMs as black boxes. And just try it. Yeah, so experiment rather than theorize. Because there are a lot of things that you might think LLMs could not possibly do that they are actually very good at. And conversely, there might be some things that you think surely an LLM can do that it turns out they're terrible at.

We found that it is okay to mostly treat LLMs as black boxes. And just try it. Yeah, so experiment rather than theorize.

So one common example is that it turns out they're actually very bad at counting the number of elements in a vector. So you give it a vector like a normal R vector with five elements, it's okay. But you give it like 102 and it gets it wrong. So this is seemingly an easy task, but it turns out they're terrible at it. And this is because LLM performance is often jagged. You might think that the graph of task difficulty and model performance looks something like this, where very easy tasks are very easy for the model to do, and harder tasks are hard, and it doesn't do a good job on them. But the graph actually probably looks something like this. There are some easy tasks that they're great at, and there's some hard tasks they're great at, but there are also some easy tasks that they are terrible at.

And so we really encourage you to just try things out and experiment. Instead of trying to reason from, you know, based on how the LLM works, this is what you would expect it to be able to do.

So we encourage you to embrace the experimental processing. This is new technology, there's a lot to explore and a lot you can do. And just experiment, play around with it. You might find that you make a, you know, app or something that doesn't work out, or you thought an LLM was going to be great for something and it turns out it's not going to work. But this is useful, it's useful to fail and try again. And your attempts don't have to be successes. This is new technology, there's a lot to discover and play around with and learn. And so I would just encourage you to experiment.

Another sort of guiding principle is just to start simple and then build up understanding. We're going to focus on pretty simple tasks today that, and some of them might, you know, seem a little silly or not necessarily relevant to your job. But the goal is for you to build intuition about how LLMs work, how ellmer and other R packages that work with LLMs work. And just to get some experience building with these things, so then you can apply those skills to your own work or your own goals.

Live chat and shinychat

Okay, we're going to switch gears really quickly and before we move on to the next section. But you might be wondering, what if you want to keep chatting back and forth? I showed you how to call the chat method and send a request to the LLM, but that seems a little clunky because you might want to keep chatting back and forth to the LLM like you can do in ChatGPT or Clod or whatever. It's pretty easy to do this with ellmer as well. There are two functions that can help you do this, live console chat and live browser, live console and live browser. So live console will create a continuous chat in the console and live browser will set up a little shiny app like ChatBot.

Just know that if you run live console or live browser, these are really easy ways to interact, to chat with LLM more interactively from R. But now we're also going to talk about shinychat. So shinychat is a way to add an AI chatbot to a shiny app or LLM capabilities to a shiny app. It's available for R and Python. We're going to only talk about R today, but it's useful to know. And shinychat and ellmer work together. So like I said at the beginning, ellmer is going to be sort of the core of everything we're going to do today. You're going to see that code that we talked about in the beginning over and over again. ellmer is going to handle the LLM API call, essentially the communication back and forth to the model. And then shinychat is going to provide the chat interface for shiny apps and some of the other logic that you need.

Okay, we're not going to talk too much about this, but I wanted to show you how to set up a basic shinychat app. If you don't know shiny, you've never used shiny before, that's okay. Just know that shiny is a way to build web apps from R. And this is sort of boilerplate shiny code. So this is making a basic app. To make it a shinychat app, we add the packages. And then we only need really two functions from shinychat to make this work. Chat mod UI, which is going to create the UI for the chat in our app, like the little chat bar, and then set up a place for the conversation to appear. And then chat mod server is doing sort of the server logic that's going to make everything worse, everything work.

And then the last thing that we need is the thing at the beginning that I said you were going to always need, which is this chat object. So here we're setting up a chat open AI. And this is the same thing we did before. It's called client here to avoid confusion with the chat ID, but it's the same code. You pass that to chat mod server. And if you run this code, you'll have a working chat bot in your shiny app. So I'm not going to run this because you're going to try this out on your own.

Exercise: word guessing chatbot

So the next exercise is to open up through O3 word games. There is a basic shiny app snippet and a system prompt in there. And your goal is to create a chat bot that plays the word guessing game with you. But you are guessing the word this time.

OK. And again, if you want to look back at the code that I've had on these slides to help you out with the exercise, everything, all these slides are on that website.

And I will take a look at the chat to see if there's any questions I can answer. Yes, sir. We do have a few questions if you want to take some of those while we're working. I think the first two that I see here may have been going back to that really great tokenize their app that you share. That was really interesting. First question from Saurabh is what about any words that might have some different meanings depending on the context in English, such as ship for transporting stuff or ship as in like I'm going to send something to you? What's been your experience and how the LLMs kind of pick the right one depending on that?

Sorry. They're pretty good at handling that kind of thing. And this is partially, I think at least because of what I was saying earlier, where they're not just looking at, you know, what is the word right before it and then what's the word is going to put next. It's looking at the entire context in these longer range dependencies to figure out what's going to make sense. And so because of that, they're pretty good at mimicking those kinds of language differences. And I feel like you'll find this out if you just chat a lot with any LLM. I feel like they're surprisingly good at that sort of thing. But again, if you have like specific things you're curious if they're going to get right, just try it out. You can either, you know, chat a normal chat to the interface or try it out with LLM and just experiment and play around.

Yep. And then one other one on that tokenizer app is that there was a look like it was looking at the possibilities for even letters in a word as well, such as when it had the word fell, it actually had a different coloring for the letter F versus ELL in the word. That was quite interesting. Any insights on that? I guess. So other times I've run this, it hasn't given me that exact limerick, obviously. But I guess F is one token, or at least in this context. And then the ELL was another token. That was the only word that was split into two tokens. So it's not necessarily that it's like looking letter by letter, but just that for that word, it happened to be two tokens. Yeah. So token does not necessarily correspond to a word.

Q&A: packages and providers

Excellent. Excellent. And then now we're getting into some of the packages themselves that we're utilizing here. Raj asks, what's the difference between the shinychat package that we're looking at here versus another package called QueryChat that we've seen from Posit? Yeah. So yeah, I wish we had more time to talk about QueryChat. So QueryChat uses shinychat. So shinychat is a sort of general way to add LLM capabilities to your Shiny app. QueryChat is a more specific package that lets you ask in natural language questions about your data and then update the Shiny app accordingly. So if we have time, I can show a demo of this. But for example, like if you have an app with some plots on it and then you'll have a conversation in the sidebar, you can say like, show me only data from Thursday. The LLM will do its thing and then update the plots over on the right-hand side. So QueryChat helps you make apps like that.

Okay. Thank you. Yeah. Yeah. And if I'm not mistaken, I think it was a year ago at PositConf, Joe Cheng had shown like a very early stage of QueryChat with a restaurant tipping example. And certainly that blew me away along with many others in the community. So it's great to see QueryChat out there.

Rodriguez is asking, what's the difference between API tokens from OpenAI or OpenAI's API and tokens from say the chat GPT browser itself, if that makes sense. I'm not quite sure either. I mean, they're all the same models. So like if you use GPT-5 in the UI versus from the API, like it's the same underlying model. So I would think that it would be the same, like it would break the tokens down the same. It's the same underlying model. Maybe there's nuance to that that I don't know, but I would say like your experience is probably not going to be meaningfully different at the token level at least.

I think I may know Ivo's answer. He's confirming. QueryChat actually is getting SQL statements from the LLM that can be used then to do the filtering. So kind of client side, so to speak, in the application. And whereas if you just – the shinychat, you're just getting the verbatim text from an LLM model when you're getting the answer back. Is that very accurate?

I guess like – so I'd say mostly. It is correct that in QueryChat, like the LLM is providing SQL, which is being used, which is part of the power of QueryChat. We'll talk about this when we talk about tool calling. But like the LLM is sort of always returning text. That's kind of all it does. And then you have to do something with that text for the code to get run. So they're both sort of returning text. And it's not really that the package is returning text. It's that like the LLM is returning text and these packages are doing stuff with it. But you're correct that it's doing something with SQL, which is sort of the power of QueryChat and why you might want to use it. It writes SQL queries that then get run on the data. And then the data is updated. And then you can see filtered data in your app based on your request.

Providers, models, and ellmer

So let's see. I'm not going to show the code for this one just in the interest of time. We're going to return to this example in a little bit. So you will see the code in another forum in a second. But also, if you have any questions or you weren't able to get this working, like I said earlier, the solutions are also in your RepositCloud project. So you can always take a look at the code there.

Great. Okay. So now we're going to talk about a little bit more detail about programming with LLMs. I'm going to go a little bit fast through the first part of this section so we can get you doing some more exercises.

But first, we're going to talk about providers and models. So now that you know how to use ellmer, let's talk about your options for different providers and models. So first, just to clarify, a provider is a company that hosts and serves models. So this is like OpenAI or Anthropic or Google. And then a model is a specific LLM that has particular capabilities. So an example of a model is CloudSonic 4.5 or GPT-5.

I'm going to kind of skip over these slides. But different models are different. You can give them different amounts of tokens. Different ones are faster than others. Different models cost different things. Some of them are smarter than others. Some of them have different abilities. There's lots of information that you can find about the different models. There's lots to know. And again, I would encourage you just to experiment and find which ones work the best for your particular needs.

Generally, when picking a model, you might want to trade off cost, speed, and intelligence. But generally, I recommend just pick one of the most recent frontier models, especially when you're just getting started. And these would be things like GPT-5, CloudSonic 4.5, or Gemini 2.5 Pro. This might depend on what provider your work is giving you access to or which one you want to pay for. So I'd say generally, when you're getting started, I wouldn't recommend picking a model that we know is pretty good and play around with it. And then if you find that you really need to worry about cost-effectiveness or speed later, you can experiment.

Okay, so let's see how to actually use the different providers and models in ellmer. ellmer supports all the major providers. There's functions for OpenAI, Anthropic, Gemini. And then you can also use local models with ellmer. So these are models that you've downloaded to your computer or some other computer and you're actually running on your machine. And then ellmer also has functions to support enterprise options. So through work, you might have AWS Bedrock, and that's how you are getting access to LLMs. You can use that way also.

Okay, that being said, the newest version of ellmer makes it even easier to switch between models and providers. So you don't even have to use different functions. You can just use the chat function and then provide the provider as a string. So it's the same function with different arguments. So this one, we're setting the provider to Anthropic, and then it just picks a model for us if we don't provide a model. Same with OpenAI. If you want to provide a particular model, you can use the provider slash model name format.

Exercise: comparing models

Okay, so in the fourth exercise, you're going to use ellmer to list all the available models from Anthropic and OpenAI. I think the code gives you the names of the functions. And then you're just going to send the same prompt to different models and compare the responses. This is more just like an experimental thing, like just play around with it, see how they do, see what kinds of models are available.

And I will look through the chat. Yep, looks like we got a couple new ones here. You may have touched on this a little bit earlier, but Somya is asking, how can we connect to our company's internal AI? Well, I guess that would depend on what they mean by internal AI. I can speak to what you just said about AWS Bedrock. A lot of organizations are using either that. I know Azure also has their own version of that as well for enterprises, I believe. And if it is a custom solution, you're going to have to get whatever, if that internal solution has a way of interacting via an API key or something similar to that in order to be, I would say, even closely compatible with what ellmer can provide. But maybe Sarah, maybe take that. If someone does have a custom internal AI solution, is it even possible for ellmer to connect to it or would they have to build their own ellmer-like package to do it?

Yeah, I guess I think it depends on what you mean by internal AI. Like if this is sort of a custom agreement that your company has with something like OpenAI, you likely can use ellmer unless you have something extremely custom going on. If your company has trained its own LLM, I'm not sure about that. I can get back to you. But I don't think that's common.

Yeah, absolutely. And then somewhat along those lines, Sass is asking, we've often heard about, especially in enterprises, Microsoft Copilot being used quite a bit in various services. Does that itself have an API that can be queried with a package like ellmer? Or is that like its own service that's, in essence, kind of a black box? Yeah. So my understanding is that Copilot, like it doesn't have its own models. It is a sort of interface to other providers' models. Like you can use GPT models through Copilot. So I think you can use the just normal ellmer functions to use these APIs, or at least you should be able to soon. There's a concept of like OpenAI-compatible APIs. So these are not just APIs that OpenAI is controlling entirely, but that you can still use with chat OpenAI. And that might work. It might also depend on the specifics.

OK. Last quick one here, hopefully, although I don't know what this is referring to. Ryan's asking if Amazon Q is supported. And I'm not sure what Amazon Q is. Yeah. I don't think so. Maybe I, feel free to add more context. I don't know. I don't think so.

Yeah. This, I haven't heard, I just Googled it, but it quickly looks like it's its own generative AI assistant. My guess is it's wrapping a bunch of things underneath that we don't necessarily have access to. I just know from experience that Bedrock seems to be the more enterprise-friendly way to get to LLMs in an organization.

Yeah. OK. I can also do some digging and get back to you about some of these questions as well. OK. Great. I hope you are all able to play around and look at the different models and get a feel for how different ones behave. These functions where you can list different models available from different providers are really helpful because they are always releasing new models. And to get the particular model, you need like a very specific string. So I'm calling these functions all the time so that I can get that string.

Multimodal input

So, we already kind of talked about this. We're going to generally use these models. We're a little bit short on time, so I'm going to go through this pretty fast. There's, we also have a package called vitals where you can write evaluations for models. So this is very helpful if you have a particular task that you want to do with an LLM and you are interested in comparing between LLMs to decide which model to use. So this is an eval that we ran on our code generation to see how well the different models did. And you can also use vitals to compare the performance and the cost.

OK. So now we're going to talk a little bit about multimodal input. So, like I mentioned earlier, modern LLMs don't just do text. They can also handle images as well as PDFs. This is called multimodal input. You might have heard that a picture is worth a thousand words, but for an LLM, a picture is roughly 170 tokens. Images get tokenized just like text.

OK. So ellmer includes functions for working with images and PDFs, and these will have the prefix content underscore. So you can use content image file to pass an image from your local file system. So this is useful if you have an image, you want to give it to the LLM and ask it to do something with it. For example, asking it what it sees in the image. You can do the same if the image is at a URL.

This was just supposed to show you how to use content image file.

Another useful thing that you can do is pass the LLM PDFs. So this is great if you have a giant PDF and you want the LLM to understand it and then give you information from it, summarize it, do anything like that with it. And to do that, you use the same sort of formatted functions, it's content PDF file or content PDF URL.

Structured output

OK. So now we're going to talk about structured output. So this is a way to get LLMs to return data in a predictable format instead of free text. So you knew in your, like, you know, R career, you might have encountered something like this, where you have a bunch of free text and you want to extract the data from it. This is pretty messy, but you can, as a human, see that there are names and ages in here that you might want to get out. This is kind of hard to do in R. You have to write a lot of code. It might look something like this. This is kind of hard to do in LLM. Very painful. And then at the end of all of that, you might still get a bunch of NAs just because the data is so messy.

The LLMs are great at this kind of task. Extracting data from extremely, you know, messy strings is very easy, generally, for an LLM to do. So with the LLM, you can just ask it to extract the name and age. And this is much simpler. Here we just set up our chat, and then we're just passing it the elements of that vector. And it gives us pretty reliable data.

But this isn't really all the way there. We might want to get, like, an actual R object out of this, like a list, and something like this. And to do this, use the ellmer function or the chat method, chat structured. And then we are passing it that first element of the vector, and that's giving us back a list. This is very useful if you have sort of very messy data and you want to get structured data out of it. The LLM will look through, pull out the relevant bits, and then put it in your data structure.

If you want to be more specific about the type of data that you want to get back, you use these type functions to provide a data structure specification. So you can be really specific about the kind of data that you want to get back. And then again, we're calling chat structured, and now we're getting a list just like we wanted, with a name element and an age element. Oh, this is a character vector, sorry, not a list. There's a lot of type functions that you can use, so you can craft your specification exactly how you want it to be. These are all, have the prefix type.

And you can also use the description argument in these type functions to tell the LLM a bit more about what this piece of data is.

Prompt engineering

Okay, so we're also going to skip this exercise. I don't know if I mentioned this earlier, but if you want to save a permanent copy of all of your work today, at the top of PositCloud, you should see a little Save a Permanent Copy button, and you can click that to save this. So you can go back to these exercises and do them on your own.

But I want to have enough time to get through all the tool calling, and this is sort of, I'd say, less important. Okay, next we're going to talk about prompt engineering.

Okay, so prompt engineering is the process of generally getting LLMs to do what you want. We talked about this a little bit at the start when we asked it to, you know, put things in the format of a HAKU or a LIMIC, but we're going to go into more detail now.

Okay, so just a reminder, you can set the system prompt using the system prompt argument in the chat function, and you can either do it by, you know, directly passing a string, or, and then providing that to chat, or putting your prompt in a file. This is generally what we recommend to do, is put your prompt in a markdown file. This makes it easier for you, the human, to write, and other humans, you know, to look at. It lets you write a lot more text, and it lets you structure it better. And then you'll just need to read it in some way, and then pass that to the system prompt argument.

Okay, and I want to emphasize that you can get a lot out of just adjusting the system prompt. And generally, if you do something with an LLM, and it doesn't work, here are three questions to ask yourself before, you know, giving up or trying something extremely fancy. First is that, did you use the best models? Are you using the latest models that are known to be good at the kinds of tasks that you want to do? And did you clearly explain what you wanted the model to do in the system prompt? Did you tell it exactly what you needed, and provide examples?

So this is, as you'll see in a minute, examples are very helpful for the LLM. Say generally, you can kind of think about designing a system prompt just like giving instructions to a human. It's not really that different. There are some things, you know, that LLMs like to have in the system prompt that you might not give to a human, but the process is generally the same. You want to be clear, you want to provide examples, you want to structure the prompt well, and you want to be very explicit about what you want.

designing a system prompt just like giving instructions to a human. It's not really that different.

Okay, you might ask, what's the difference between the system prompt and the user prompt? Meaning like the questions that the user themselves is asking. The short answer is that anything that you want to persist, like background knowledge or instructions that you want to cover all the LLMs responses, put that in the system prompt. Anything that is like for one off request can go in the user prompt, or like you can tell the user that they need to like put that in their query.

Okay, some tips for writing system prompts. First is that you can use LLMs to help write your prompts. Clod has a pretty good prompt generator that you can find if you search Clod prompt generator, but you could also just like use a general LLM and just ask it to help you write a prompt. And they're pretty good at this. The other thing is to add structure to your prompt. Use markdown headings or xml tags to add structure. You can also use variables, that's what's in these little curly brackets here, to insert dynamic content into your prompts or just like content from that you want to live in other files. This could be a security concern, so just be aware and make sure you're not inserting content that like you know is open to the public or that might have nefarious content in it.

Okay, like I said, we really recommend putting your prompts in separate files. This is better probably for the LLM, but also just for you as a human, especially if you're sharing those prompts or putting them on github or anything like that. And you can have multiple files, you know, either for different uses or one prompt that's reading in information from different files. This is a much better way to organize it than putting it all in one string in your script.

If you've done all this and it still isn't working, one tip is to force the model to say things out loud or, you know, say things out loud in an LLM way, which is sort of like echoing information. But this is helpful for getting the LLM to like kind of stop and think about what it's doing. When we talk about tool calling in a little bit, one thing that sometimes happens is that LLMs will not call the tools even though you've asked them to call a tool and it might like act as if it's called a tool when really it hasn't. Asking it to say what it has done and what or what it's going to do can be helpful for getting the LLM to behave exactly how you want if there are complex rules or constraints.

If you want to learn more about prompt engineering, these are some useful resources. ellmer also has a prompt design vignette on the ellmer website. I can share the link to that in a second.

Okay, we have time for this. So now you're going to play around with assistant prompts. This is a nine quiz game one. So your goal is to teach the model to play a quiz game with you. I've started the prompt for you and the entirety of this exercise is just writing in that markdown file. You're just writing text, there's no code. So any like debugging you do, it's in the system prompt itself. Your goal is just to write words that get the LLM to do what you want it to do.

I will quickly show you what this game should look like so it's a little bit easier to understand. So it's this little quiz game. Your job is to ask it to come up with five themes. It doesn't need to be these five themes. So then the user picks the theme and then the LLM asks different trivia questions.

And then it gives another round. Okay, so your job is to put all this information in the prompt so it behaves like you want it to.

For the R version, you probably want a recent version of R. Using a very old version, some things might not work as expected. So just generally, not necessarily for this with new packages.

The question about prompt engineering, fine-tuning, and RAG is interesting. I'd say it's sort of like a ladder, like try prompt engineering first. Do not move directly to fine-tuning or RAG. If you really cannot do what you need to do with prompt engineering, then you would move on to one of those other methods. But in general, I'd say there's a lot that you can do with prompt engineering and moving to these more complex, higher investment techniques is better to do after you've exhausted what you can do with prompt engineering. And that might take some experimenting. There's not a clear divide between when you definitely can't do it with prompt engineering and you need to move to fine-tuning. You might have to do some experimentation.

I'd say it's sort of like a ladder, like try prompt engineering first. Do not move directly to fine-tuning or RAG. If you really cannot do what you need to do with prompt engineering, then you would move on to one of those other methods.

The question about query chat and the metadata versus the underlying data, I'd say if you ever have security concerns, you should take those seriously with LLM things and verify with yourself and your security team at work, especially if you have sensitive data. But generally for query chat itself, part of what makes it very useful is that it is only being shown the metadata and it can only do things that it knows how to do based on the metadata. So the data is not being shown to the LLM in any way. There is, of course, there's an open sort of chat window. So if you as the user are pasting in data, that will get sent to the LLM. But at the back end of query chat, it just sees the metadata. And so it just knows how to construct SQL queries based on the format of the data, the column names and the types like, you know, how the data is structured. So it's not seeing the actual data file.

OK. I hope everyone was at least able to get part of the game working. You can take a look at the in the solutions folder. There is a solution prompt so you can see how we structured it. It has some examples and it's very straightforward, but there's nothing particularly fancy going on. And the primary point of this exercise is just to show you like how much you can control with the system prompt and like things that you are you might be used to controlling with code. You can move to controlling with words in the system prompt.

Tool calling

OK. So the last 25 minutes, we're going to talk about tool calling.

OK. So as a recap, let's just think again about how LLMs work. This is going to be important for understanding why we need tools. So generally, with an LLM, you write some words, the LLM writes some words back to you, and then you do something with those words. You know, you use the code that it generated, you paste the poem somewhere, you use that information to inform what you're doing, that kind of thing. But this is all word based. So does that mean that LLMs can do things like access the Internet or run code, send an email or like generally interact with the world?

Let's try it out. What if we ask it something that would not be in the training data, like who are the keynote speakers at our Pharma 2025? So I asked GPT 401 Nano this the other day, and it says my training data only includes data up to October 2023, and it doesn't have access to real time updates. So basically, it doesn't know and it can't answer. What about if we ask it what the weather's like? It says, again, basically it can't, it doesn't know how to provide real time weather updates. Or if we ask it something that seems very easy, like what day is it? It doesn't know that. This was yesterday and it told me it was October 27th, 2023, which is the cutoff date from its training, which is why I perpetually think it is October 27th, 2023. This is sort of particularly interesting because the model didn't even say it doesn't know, it just gives you the wrong date.

This seems like a major limitation of LLMs, like they can't even tell you what the day is, and they can't interact with new things on the Internet. They don't know what the weather is like. But luckily, there is a solution to this, which are tools. So tools, you can think of essentially as functions. They are functions that we're giving the LLM that give it new abilities. You can use tools to give the LLM access to up to date or real time information, like, you know, real time weather. And you can also use tools to let the model interact with the world. You can build a tool that lets the LLM run code or interact with your file system, browse the Internet, that kind of thing. So generally, these are like add-on abilities for the LLM. You're giving it abilities that it wouldn't otherwise have.

How does tool calling work? We're going to walk through the process at a high level, step by step. So earlier, I asked it what the weather was like in Minneapolis, and it said it didn't know. But what if the LLM has the appropriate tool? So if I asked it something like, what should I wear today in Minneapolis? And it has access to some kind of tool that can provide weather information. It is going to request that that tool is called for, this is a zip code for Minneapolis. So it requests that this tool is called. And then this is sort of the important part. The LLM itself is not calling the tool, it's not calling the function, it's requesting that the tool is called. And then your computer, a computer somewhere, is running that tool, pinging the weather API, that sends back data about the weather.

The LLM receives that data, it takes a look at it, and then it sends you back what you should wear. I don't know if this is what you should wear, if it's 58 degrees, but this is what the diagram says, we're going to go with that. And this looks a little complicated, but all of this, like all the stuff in this little triangle here, this is all handled by ellmer. So you're going to see in a minute, you don't really need to worry about like what's happening with the communication back and forth to the LLM. The main thing you need to do is write the tool function itself.

And again, I want to emphasize that the LLM can't run code by itself. Like I've been saying, or trying to emphasize, like the LLM, it's just like text. It takes text in, you know, generally it's providing text output. And it doesn't have the ability to run code. It needs something like, you know, your R session to run code in for it to be able to do stuff like this. The LLM is not executing the tools on its own. It is asking you or like your computer to run them. But so then you might think like, well, I could already do that. I could already run a function that things a weather API on my own. Why do I need an LLM if it can't even run code?

And the purpose is that what the LLM can do is be very good at deciding when and how to call a tool. So instead of you needing to figure out what zip code to pass to get weather, the LLM does that, can do that for you. And it can figure out when it needs to call get weather and when you're asking it something that maybe doesn't require knowledge about the weather. So the LLM is choosing when and how this tool is going to be called. And so based on your response, it would request different kinds of tool calls. If you ask it about San Francisco, it's going to pick the appropriate zip code. And you might also have more complicated tools that have multiple arguments and the LLM can pick all of those arguments for you. Based on your request, LLM can also call multiple tools if it needs to gather different pieces of different information from different spots. So again, the sort of like magic of what the LLM doing is in how and when it is calling the tool.

Okay, so let's take a look at an example. You have this demo in Fawcett Cloud as well. So you can order something on your own if you want. Okay, so this is an app that is hooked up to a weather tool. So let's see how this works. Earlier, if you remember, I asked it about the weather and it basically said, I don't know. So now, let's ask it, what's the weather in Minneapolis? And it tells us the current weather is currently around 54 degrees. And it is also showing us that it did a tool call. This weather function works a little bit different than the one that I put in the diagram where it's asking for a Latin long instead of a zip code, but it's the same idea. The LLM has decided which Latin long to pass to the function to get the data that it wants. And it gets some information back. And because it's good at synthesizing information and producing responses, it takes all that in and then crafts the response that you see below. I think there's two tool calls here because it's doing it for slightly different Latin longs.

Writing tools in R

Okay, so I'm just showing you how this works so you can get an idea of how an LLM might incorporate a tool call into its response. But now we'll take a look at the code. I'm going to show you how to write tools in R. And this might seem complicated. You're giving an LLM a new ability. I want to emphasize that most of this is writing in R functions. If you can write a function in R, just like a normal everyday R function, you can write tools for an LLM. Because the first step is just to write a normal R function.

If you want to provide the LLM with a tool where you can get weather information through a weather API, you might write a function called get weather that takes a zip code or some kind of location information. And then I just didn't fill this in to make it easier to understand what's happening. But you would have some just normal R code that's sending a request to the weather API and getting data back. These should be functions that you can test out yourself. You can run them in the console, run them with normal inputs, like you as a human. There's nothing really special about it. It's just an R function.

And then the next step is to define the tool. So now we're getting into more LLM specific things that you need to do to create the tool. So tool is a function from the ellmer package. And this is how you're going to turn a normal R function into a tool. So you pass the function. This is just a stand-in for the function that is your tool. So the name of the function. And then the rest of this is basically documentation for the LLM about how to use your tool. You might notice that we're doing a lot of things that feel like documenting. You wrote a system prompt, which kind of feels like documentation. And now we're writing function documentation. I think there's a lot of crossover between getting LLMs to do what you want and providing useful information to help people use your functions or tools or products. Often it feels very similar. So all of this is essentially documentation for how your tool works. You have a description and then your arguments. So it's going to be a list of the various arguments your tool function can take in their types. You can also say if those arguments are required with the required argument.

So this is the same thing but for our getWeather example. So again, we asked the name of the function, getWeather, a description, and then a list of arguments and their types. This function only takes one argument, zip code, and we want it as a string.

Okay, so we wrote a function, defined the tool. The third step is to register the tool. The chat object has a method called registerTool and you just pass in that tool object. This tells the chat object that the LLM can use this tool. It's available and it passes along all of that information, all the documentation.

We have time to do this. So you're going to open Quiz Game 2. This is building on the quiz game you just wrote. It has a prompt already there so you don't need to do anything with the prompt. There is a function in that app file called playSound that plays a sound when you call it. Your job is to create a tool that uses this function and then hook it up to the LLM. So register the tool so that in your app the LLM can request that this tool is called and there will be a sound that plays.

I might call you back a little bit earlier than six minutes, but we'll start the clock anyway.

Hi, Sarah. Do you mind taking a question from the chat? I can read to you. Yeah, sure. Jared's asking what is your favorite LLM to use right now? Yeah, I was just answering that in the chat. I'd say generally for things I just use Cloud Sonnet 4.5 for everything. That's partially because of how we have things set up in Posit, but also it's good at most things. That's the latest Cloud Sonnet model.

I guess a follow-up into that, how much have you ventured into vibe coding recently? I think it's a really great tool to be able to using LLMs to help you code is very useful. I think I'm wary of 100% vibes and no understanding or oversight. Not that you can't do that and that's not going to work, but I like to make sure I still understand things. But if you're using tools like Cloud Code, Codex to speed up your coding or help you write better code, I think it's very useful and can let you do things that you wouldn't have otherwise been able to do, which is pretty cool.

Very useful. I feel like the downside of things being called tools, does that mean tools like function tools that we're hooking up to the LLM or tools like LLM tools?

There's a couple R packages that we're not going to have time to talk about today that are essentially collections of useful tools for R. There's BTW. I'll get the link.

This is the BTW page. This has a variety of tools that make it easier to do things in R. These aren't tools that I wrote, but they are useful tools stored in a package.

We're just going to move on just for time, but we might have a minute at the end for additional questions. I hope you all got this working, or at least were able to experiment with the code. Again, you can take a look at the solution if you got stuck.

Tools in Shiny

Oh, I think I was looking at the wrong timer. I might have cut you off early, but regardless, it's over now. Okay. This slide says tools in Shiny. You just wrote a tool for Shiny app. That was a Shiny app, but just some things to keep in mind. The tool function goes inside the server function. A useful thing that you can do is update reactive values with this tool. If something changes, you can write a tool that updates a table or updates a plot or something like this.

You can see this logic if you go through the query chat code. This is what's happening. This is very useful because you can have the user say something in the chat, and then you can have a tool that will update reactive values like a data frame.

Okay. There are a lot of packages that we weren't able to talk about today. I know there was a lot of questions about query chat. I don't know if there's a sticker, but we also have a variety of open source R packages for working with LLMs. We talked about ellmer and Vitals in shinychat. I just mentioned BTW. There's also Ragnar for Ragnar. I have the chat list here. It's a Python package, but it fit in the landscape. Chores, which is another tool collection package.

Encourage you to check out these other packages if you want to learn more.

Okay. And then I also have a link to query chat. We talked a bit about this, but it's a really useful package. There's also a package called ggbot2, which lets you talk out loud to create and iterate on plots. This is useful to look at and play around with, but it's also a useful example of an app that uses speech to do something. You might extend that for your own uses.

We also have a tool, general definition of a tool, called DataBot, which is an EDA assistant for Positron that I've linked here. And then if you're interested in keeping up with AI news coming out of Posit, along with my coworker, Simon Couch, I write an AI newsletter that we release every other week. I have the link to it here. This is Posit news, but also we cover sort of general AI news.

Okay. And yeah, thank you. This was great. I enjoyed answering all your questions. And I hope you got some use out of this workshop. Again, if you want to save a copy, click that save a permanent copy button at the top of Posit cloud. And at the end of this workshop, unfortunately, we will shut off those API keys. So you will need to get your own if you want to run the code. And you store those in our environment file.

Closing remarks

Great. I guess we have one minute, if there's anything I can answer. But otherwise, thank you for coming. This was great. Well, thank you so much, Sarah. We know how much work it is to prepare for these workshops. And we had a lot of attendees, you know, very engaged into all the material here. Again, a lot of thank yous and kudos in the chat. Well-deserved.

I'll put this in the chat one more time in case you missed the badge credential link. But otherwise, yeah, hopefully everyone can join me virtually and say thank you so much, Sarah, for this great material. And again, we'll make sure that we will have the recording this workshop posted on the rPharma YouTube channel, hopefully in a month or so, along with all the great links that were shared throughout Sarah's presentation, as well as in the chat. You may want to save that chat if you want to get a lot of those links on your own and not wait for us to get those. But yeah, Sarah, yeah, any last parting words for our attendees as they begin their LLM journey with open source?

I guess one thing, I tried to say this a couple times, but I would just encourage you to experiment with the tools and play around with it. I think sometimes it's easy to feel either like overwhelmed by LLM things or like, you know, it's kind of pointless because the LLM can just do things for you. But there really is a lot that you as, you know, an R user can do yourself, new things to build, new things to experiment with. So, I just encourage you to be curious and experiment with the tools.

But there really is a lot that you as, you know, an R user can do yourself, new things to build, new things to experiment with. So, I just encourage you to be curious and experiment with the tools.

Yes, it felt a little intimidating for me in the very beginning, but certainly thanks to your colleagues at PASA and all of your material, you can start small and get pretty far pretty quickly. And in fact, last year at our pharma conference, I built an app that was heavily influenced by LLMs to generate random facts about haunted places because our pharmas are on Halloween. So, lots of fun activities you can, or lots of fun domains you can do this.

Well, okay. Well, we'll wrap it up there, but thank you so much once again, Sarah. And yeah, for those of you online, we got another workshop happening right now about the use of the Cardinal package for clinical reporting, and there's a whole great lineup as well tomorrow. So, thank you so much, and we will see you later in the R-Pharma conference. Thanks, Eric. Thank you.