<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Twilio</title>
    <description>The latest articles on DEV Community by Twilio (@twilio).</description>
    <link>https://dev.to/twilio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F312%2F0bfb1ba6-a192-44af-8231-4f6b07e582a8.png</url>
      <title>DEV Community: Twilio</title>
      <link>https://dev.to/twilio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/twilio"/>
    <language>en</language>
    <item>
      <title>Create AI-generated art via SMS with Replicate in Python</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Thu, 21 Sep 2023 21:22:37 +0000</pubDate>
      <link>https://dev.to/twilio/create-ai-generated-art-via-sms-with-replicate-in-python-1gkd</link>
      <guid>https://dev.to/twilio/create-ai-generated-art-via-sms-with-replicate-in-python-1gkd</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/ai-art-sms-replicate-python" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's fun to get creative! Read on to learn how to create AI-generated images via SMS using &lt;a href="http://replicate.com/" rel="noopener noreferrer"&gt;Replicate&lt;/a&gt; and &lt;a href="https://www.twilio.com/docs/sms" rel="noopener noreferrer"&gt;Twilio Programmable Messaging&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fc2ccvqvngfpipo5cua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fc2ccvqvngfpipo5cua.png" alt="Prompt via SMS is Pikachu playing tennis and the image returned is of Pikachu on a tennis court with a tennis ball" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you prefer learning via video more? Check out &lt;a href="https://www.tiktok.com/@lizziepikachu/video/7281387478328610094" rel="noopener noreferrer"&gt;this TikTok summarizing this tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A Twilio account - &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up for a free Twilio account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Twilio phone number with SMS capabilities - &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;learn how to buy a Twilio Phone Number here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Replicate account to use the Stable Diffusion text-to-image model – &lt;a href="https://replicate.com/signin" rel="noopener noreferrer"&gt;make a Replicate account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python installed - &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;download Python here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, a handy utility to connect the development version of our Python application running on your machine to a public URL that Twilio can access.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ &lt;strong&gt;ngrok is needed for the development version of the application because your computer is likely behind a router or firewall, so it isn’t directly reachable on the Internet. You can also choose to automate ngrok as shown in this article.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Replicate
&lt;/h3&gt;

&lt;p&gt;Replicate offers a cloud API and tooling so you can more easily run machine learning models, abstracting away some lower-level AI concepts and handling infrastructure so you can focus more on your own applications. You can run open-source models that others have published, or package and publish your own, either publicly or privately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Since you will be installing some Python packages for this project, you need to make a new project directory and a &lt;a href="https://docs.python.org/3/tutorial/venv.html" rel="noopener noreferrer"&gt;virtual environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're using a Unix or macOS system, open a terminal and enter the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;replicate-prompt-to-image-sms 
&lt;span class="nb"&gt;cd &lt;/span&gt;replicate-prompt-to-image-sms 
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate 
pip &lt;span class="nb"&gt;install &lt;/span&gt;replicate flask twilio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're following this tutorial on Windows, enter the following commands in a command prompt window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;replicate-prompt-to-image-sms  
&lt;span class="nb"&gt;cd &lt;/span&gt;replicate-prompt-to-image-sms  
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate 
pip &lt;span class="nb"&gt;install &lt;/span&gt;replicate flask twilio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab your &lt;a href="https://replicate.com/account/api-tokens" rel="noopener noreferrer"&gt;default Replicate API Token or create a new one here&lt;/a&gt;. Run this command in the terminal in your current folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REPLICATE_API_TOKEN&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;replace with your api token&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's time to write some code to receive a prompt via SMS and return an AI-generated image!&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn a Prompt into an AI-generated image via SMS with Replicate
&lt;/h3&gt;

&lt;p&gt;Make a file called &lt;em&gt;app.py&lt;/em&gt; and place the following import statements at the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;replicate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twilio.twiml.messaging_response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessagingResponse&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, make a Flask app so your app can receive the inbound text message to your Twilio phone number. That string is then passed to &lt;a href="https://replicate.com/stability-ai/stable-diffusion" rel="noopener noreferrer"&gt;Replicate 's stable diffusion model&lt;/a&gt; to generate photo-realistic images given that prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/sms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;inb_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;replicate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stability-ai/stable-diffusion:27b93a2413e7f36cd83da926f3656280b2931564ff050bf9575f1fdf9bcd7478&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inb_msg&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt was &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;inb_msg&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res_prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the command line, run &lt;code&gt;python app.py&lt;/code&gt; to start the Flask app. Now it's time to set up a Twilio phone number so you can text it a prompt and get an AI-generated image back!&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure a Twilio Number for the SMS Chatbot
&lt;/h3&gt;

&lt;p&gt;Now, your Flask app will need to be visible from the web so Twilio can send requests to it. ngrok lets you do this. With ngrok installed, run &lt;code&gt;ngrok http 5000&lt;/code&gt; in a new terminal tab in the directory your code is in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9ca99dfuftcf76hokcs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9ca99dfuftcf76hokcs.png" alt="ngrok terminal after running the command ngrok http 5000" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see the screen above. Grab that ngrok &lt;strong&gt;Forwarding URL&lt;/strong&gt; to configure your Twilio number: select your Twilio number under &lt;strong&gt;Active Numbers&lt;/strong&gt; in &lt;a href="https://www.twilio.com/console/phone-numbers/incoming" rel="noopener noreferrer"&gt;your Twilio console&lt;/a&gt;, scroll to the Messaging section, and then modify the phone number’s routing by pasting the ngrok URL with the &lt;em&gt;/sms&lt;/em&gt; path in the textbox corresponding to when &lt;strong&gt;A Message Comes In&lt;/strong&gt; as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk1ibibep57vql4jnf0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk1ibibep57vql4jnf0b.png" alt="Configure Twilio phone number with ngrok URL when a message comes in in the Twilio console" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt; and now your Twilio phone number is configured so that it maps to your web application server running locally on your machine and your application can run. Text a prompt of a picture you'd like to generate to your Twilio number and you should get an AI-generated image of whatever you sent over SMS!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1dy9k71v16cwyxla1pa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1dy9k71v16cwyxla1pa.png" alt="Prompt via SMS is Pikachu playing tennis and the image returned is a different one than the first one at the start of this tutorial of Pikachu on a tennis court" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of ngrok, you could &lt;a href="https://www.twilio.com/blog/expose-localhost-to-internet-with-tunnel" rel="noopener noreferrer"&gt;alternatively use another tunneling service like Tailscale&lt;/a&gt;–my teammate Rishab reviewed this post, and kept getting a 403 error with ngrok. The solution was to switch port numbers with ngrok–it seems that Tailscale has more privileges to the network settings compared to ngrok. You can view the &lt;a href="https://github.com/elizabethsiegle/replicate-prompt-to-image-sms/tree/main" rel="noopener noreferrer"&gt;complete code on GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for Replicate and Twilio Programmable Messaging?
&lt;/h3&gt;

&lt;p&gt;There is so much fun for developers to have around building with LLMs! Replicate offers so many models to play with like &lt;a href="https://replicate.com/stability-ai/sdxl" rel="noopener noreferrer"&gt;sdxl, a text-to-image generative AI model&lt;/a&gt; that creates beautiful 1024x1024 images, or you can fine-tune your own. I can't wait to see what you build with AI–let me know online what you're working on!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter: &lt;a href="https://twitter.com/lizziepika" rel="noopener noreferrer"&gt;@lizziepika&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/elizabethsiegle" rel="noopener noreferrer"&gt;elizabethsiegle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:lsiegle@twilio.com"&gt;lsiegle@twilio.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>replicate</category>
      <category>twilio</category>
      <category>stablediffusion</category>
    </item>
    <item>
      <title>Build an AI Personal Trainer with LangChain Agents and SendGrid</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Tue, 19 Sep 2023 19:53:32 +0000</pubDate>
      <link>https://dev.to/twilio/build-an-ai-personal-trainer-with-langchain-agents-and-sendgrid-475g</link>
      <guid>https://dev.to/twilio/build-an-ai-personal-trainer-with-langchain-agents-and-sendgrid-475g</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/ai-personal-marathon-trainer-agents-sendgrid" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Can AI help me run a faster marathon?&lt;/p&gt;

&lt;p&gt;Problem: I play a lot of tennis and run, bike, and walk as my forms of transit, so I have never trained for a half marathon or 10k. However, I KNOW I must train to run a marathon. As a developer, I turned to code, wondering how AI could help me run a faster marathon.&lt;/p&gt;

&lt;p&gt;Solution: Read on to learn how to build a personal marathon training plan generator &lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt;-hosted app from personal workout data using the &lt;a href="https://developers.strava.com/" rel="noopener noreferrer"&gt;Strava API&lt;/a&gt;, &lt;a href="https://python.langchain.com/docs/get_started/introduction.html" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; agents, chains, few-shot prompt templating, PromptTemplates, &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, and &lt;a href="https://sendgrid.com/" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt;. This can also be applied to personal training, nutrition, and more!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6o5815sjnzixio1yu8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6o5815sjnzixio1yu8p.png" alt="gif of Streamlit app in action" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Streamlit page above, a user inputs their Strava API token so the app can access their workout data, the date they want to begin training, and the marathon (or race) day. When they click &lt;strong&gt;Submit&lt;/strong&gt;, a custom training plan is generated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ai-gen-marathon-plan.streamlit.app/" rel="noopener noreferrer"&gt;You can test the app out here&lt;/a&gt; using a Strava API token generated by following the directions under &lt;a href="https://www.twilio.com/blog/3-ways-query-data-langchain-agents-python" rel="noopener noreferrer"&gt;Strava Activity Webhook Authentication in this tutorial on querying data with LangChain agents&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Do you prefer learning via video more? Check out &lt;a href="https://www.tiktok.com/@lizziepikachu/video/7280558362805194027" rel="noopener noreferrer"&gt;this TikTok relating to this tutorial&lt;/a&gt;--massive thank you to Twilio Community Engagement Manager Sandra Mendez for spearheading this video.&lt;/p&gt;

&lt;center&gt; 
&lt;/center&gt;

&lt;h3&gt;
  
  
  Understanding LangChain Agents
&lt;/h3&gt;

&lt;p&gt;This meaty app uses many features of LangChain, such as agents: agents use LLMs to decide what actions should be taken and they have access to tools like web search or calculators which can be packaged into a logical loop of operations.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://www.twilio.com/blog/basketball-sms-chatbot-with-langchain-prompt-templates" rel="noopener noreferrer"&gt;more background on LangChain, you can read this blog post here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;a href="https://signup.sendgrid.com/" rel="noopener noreferrer"&gt;free SendGrid account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python installed - &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;download Python here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;An email address to test out this project&lt;/li&gt;
&lt;li&gt;OpenAI Account – &lt;a href="https://openai.com/api/" rel="noopener noreferrer"&gt;make an OpenAI Account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Strava account - &lt;a href="https://www.strava.com/register/free" rel="noopener noreferrer"&gt;sign up for a Strava account if you don't have one already&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Strava app and API key - &lt;a href="https://www.twilio.com/blog/3-ways-query-data-langchain-agents-python" rel="noopener noreferrer"&gt;follow directions here under Setup the Strava API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Strava Auth token - &lt;a href="https://www.twilio.com/blog/3-ways-query-data-langchain-agents-python" rel="noopener noreferrer"&gt;follow directions here under Strava Activity Webhook Authentication&lt;/a&gt;
You need a Strava app and API key–following the directions above will provide you with the necessary Strava Client Secret in order to generate a &lt;a href="https://developers.strava.com/docs/authentication/#refreshingexpiredaccesstokens" rel="noopener noreferrer"&gt;Strava Access Token&lt;/a&gt; to make a request to access your Strava data. Different Strava API app values include:&lt;/li&gt;
&lt;li&gt;Client ID: Your application ID&lt;/li&gt;
&lt;li&gt;Client Secret: Your client secret (please keep this confidential)&lt;/li&gt;
&lt;li&gt;Authorization/Access token: Your Strava API app's authorization token will need to be regenerated every six hours (should be confidential)
Next, after making an OpenAI account if you don't have one already, you can &lt;a href="https://beta.openai.com/account/api-keys" rel="noopener noreferrer"&gt;get an OpenAI API Key here&lt;/a&gt; by clicking on &lt;strong&gt;+ Create new secret key&lt;/strong&gt;. Then, &lt;a href="https://app.sendgrid.com/settings/api_keys" rel="noopener noreferrer"&gt;grab a SendGrid API key here&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Python application will need to have access to these keys, so in your root directory (called &lt;code&gt;strava-langchain&lt;/code&gt;) make a &lt;em&gt;.env&lt;/em&gt; file to store the API keys safely. The application we create will be able to import this key as an environment variable soon.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;.env&lt;/em&gt; file add the following lines of text, making sure to replace &lt;code&gt;&amp;lt;YOUR-OPENAI-API-KEY&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;YOUR-SENDGRID-KEY&amp;gt;&lt;/code&gt; with your actual keys. STRAVA_CLIENT_SECRET should have been used above to generate a Strava Access Token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &amp;lt;YOUR-OPENAI-KEY&amp;gt;
SENDGRID_API_KEY &lt;span class="o"&gt;=&lt;/span&gt; &amp;lt;YOUR-SENDGRID-KEY&amp;gt;
&lt;span class="nv"&gt;STRAVA_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &amp;lt;YOUR-STRAVA-CLIENT-SECRET&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Strava Access Token will be placed into a Streamlit form. Streamlit is an open-source Python app framework that helps developers quickly create web apps for data science and machine learning.&lt;/p&gt;

&lt;p&gt;In the root directory run the following commands to install the project's required libraries after making a virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make a &lt;em&gt;requirements.txt&lt;/em&gt; file containing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;chromadb&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.4.5
google-search-results&lt;span class="o"&gt;==&lt;/span&gt;2.4.2
&lt;span class="nv"&gt;langchain&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.0.261
&lt;span class="nv"&gt;numpy&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.25.0
&lt;span class="nv"&gt;openai&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.27.2
&lt;span class="nv"&gt;pandas&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;2.0.3
python-dotenv&lt;span class="o"&gt;==&lt;/span&gt;1.0.0
&lt;span class="nv"&gt;PyYAML&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;6.0
&lt;span class="nv"&gt;reportlab&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;4.0.4
&lt;span class="nv"&gt;Requests&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;2.31.0
&lt;span class="nv"&gt;sendgrid&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;6.10.0
&lt;span class="nv"&gt;streamlit&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.25.0
&lt;span class="nv"&gt;tabulate&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;0.9.0
&lt;span class="nv"&gt;urllib3&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;2.0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install them on the command line by running &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then run &lt;code&gt;mkdir data_files&lt;/code&gt; to make a folder called &lt;em&gt;data_files&lt;/em&gt; to store the activity data.&lt;/p&gt;

&lt;p&gt;We will import these in our code soon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Few-shot prompting Examples
&lt;/h3&gt;

&lt;p&gt;There are a few ways one could go about making an AI personal marathon trainer, workout trainer, or recommendation system. My college classmate-turned Redis AI engineer &lt;a href="https://twitter.com/sampartee" rel="noopener noreferrer"&gt;Sam Partee&lt;/a&gt; suggested using few-shot prompting, in which a few explicit examples (or "shots") guide the Large Language Model (LLM) to respond in a certain way.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;em&gt;stapp.py&lt;/em&gt; and include some examples of marathon training plans. I used OpenAI to create these plans and then formatted them a bit because most marathon plans I found online were not copy-and-pasteable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;examples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;training_start_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;July 19&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;marathon_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;November 8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pd_run_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The average distance is 3.71 miles and the maximum distance is 9.52 miles. The average moving time is not available and the maximum moving time is not available. Other running statistics include total elevation gain, with an average of 47.6 and a maximum of 200.6.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;num_workouts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;112&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Week 1: 
Day 1, {training_start_date}: Cross-training - Swim
Day 2: Easy run - 4 miles at an easy pace
Day 3: Cross-training - Elliptical 
Day 4: Medium run - 5 miles at a medium pace 
&lt;/span&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="n"&gt;Marathon&lt;/span&gt; &lt;span class="n"&gt;Day&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;span class="n"&gt;Note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;above&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="n"&gt;assumes&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;currently&lt;/span&gt; &lt;span class="n"&gt;able&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;comfortably&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;miles&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;easy&lt;/span&gt; &lt;span class="n"&gt;pace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Adjustments&lt;/span&gt; &lt;span class="n"&gt;may&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt;
&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;made&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;yet&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Additionally&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="n"&gt;includes&lt;/span&gt; &lt;span class="n"&gt;cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;training&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;
&lt;span class="n"&gt;ensure&lt;/span&gt; &lt;span class="n"&gt;proper&lt;/span&gt; &lt;span class="n"&gt;recovery&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;prevent&lt;/span&gt; &lt;span class="n"&gt;injury&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="sh"&gt;"""&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/elizabethsiegle/ai-generated-marathon-trainer/blob/main/stapp.py" rel="noopener noreferrer"&gt;complete code for that file is here on GitHub&lt;/a&gt;. Now let's make some custom tools to help LangChain suggest personalized workouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make Custom Tools to assist LangChain Agents
&lt;/h3&gt;

&lt;p&gt;Using agents allows LLMs access to &lt;a href="https://python.langchain.com/docs/modules/agents/tools/" rel="noopener noreferrer"&gt;tools with which LLMs can search the web, do math, run code, and more&lt;/a&gt;. Though LLMs are powerful, they still have trouble with certain tasks–that's why you may sometimes need to make &lt;a href="https://python.langchain.com/docs/modules/agents/tools/custom_tools" rel="noopener noreferrer"&gt;custom tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;LangChain offers a large selection of pre-built tools, but only so many problems can be solved using existing tools.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;em&gt;request.py&lt;/em&gt; and include the following import statements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;  &lt;span class="c1"&gt;# dataframe
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;reportlab.platypus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleDocTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Paragraph&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;reportlab.lib.pagesizes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;reportlab.lib.styles&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;getSampleStyleSheet&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;reportlab.lib.units&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;inch&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dotenv_values&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMChain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LLMMathChain&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LLMSingleActionAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentOutputParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;create_pandas_dataframe_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initialize_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentType&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.llms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;  &lt;span class="c1"&gt;# , GPT4All
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseChatPromptTemplate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.prompts.few_shot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FewShotPromptTemplate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.prompts.prompt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.schema&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentFinish&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HumanMessage&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sendgrid&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SendGridAPIClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sendgrid.helpers.mail&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Mail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Attachment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Disposition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stapp&lt;/span&gt;  &lt;span class="c1"&gt;# file
&lt;/span&gt;&lt;span class="n"&gt;urllib3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable_warnings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;urllib3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InsecureRequestWarning&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dotenv_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we set some variables such as the Strava URL to retrieve a user's Activity data, have the app access its API keys, and then chain LLMs together: the &lt;code&gt;llm&lt;/code&gt; variable passes the desired &lt;code&gt;gpt-4-32k&lt;/code&gt; model to use with ChatOpenAI and sets the temperature to 0.2–play around with this value. A higher temperature value means the LLM output will include more randomness. Lastly, pass &lt;code&gt;LLM&lt;/code&gt; to a math chain to perform complex math problems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;activities_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.strava.com/api/v3/athlete/activities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SENDGRID_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SENDGRID_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gpt-4-32k&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;llm_math_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LLMMathChain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now make some functions for the custom tools to help the LLM convert meters to miles, calculate the number of days between two dates, and get the number of days in a Pandas dataframe object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convert_to_miles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;m_conv_factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1000.0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calc_days_btwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;training_start_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="n"&gt;training_start_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;training_start_date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;training_start_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;training_start_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%d&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;training_start_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;training_start_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;marathon_date&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;training_start_date&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;#number of workouts in Strava data
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;num_rows_in_dataframe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beneath that, add a helper function to help validate user emails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;email_regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# pass the regular expression and the string into the fullmatch() method
&lt;/span&gt;    &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fullmatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email_regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's time to make a Custom Prompt Template in LangChain to make constructing prompts using dynamic inputs easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make Custom Prompt Template
&lt;/h3&gt;

&lt;p&gt;LangChain provides a set of &lt;a href="https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/" rel="noopener noreferrer"&gt;default prompt templates&lt;/a&gt; to generate prompts for different tasks. However, for some projects like this one, developers might want to create a prompt template with specific dynamic instructions for the LLM–in instances like that, you can create a &lt;a href="https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/custom_prompt_template" rel="noopener noreferrer"&gt;custom prompt template&lt;/a&gt; similar to the one below right inside &lt;em&gt;request.py&lt;/em&gt; beneath &lt;code&gt;validate_email&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#Set up a prompt template
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseChatPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="c1"&gt;# The list of tools available
&lt;/span&gt;    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Get the intermediate steps (AgentAction, Observation tuples)
&lt;/span&gt;        &lt;span class="c1"&gt;# Format them in a particular way
&lt;/span&gt;        &lt;span class="n"&gt;intermediate_steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intermediate_steps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;thoughts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;observation&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;intermediate_steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;thoughts&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
            &lt;span class="n"&gt;thoughts&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Observation: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;observation&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Thought: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="c1"&gt;# Set the agent_scratchpad variable to that value
&lt;/span&gt;        &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_scratchpad&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thoughts&lt;/span&gt;
        &lt;span class="c1"&gt;# Create a tools variable from the list of tools provided
&lt;/span&gt;        &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="c1"&gt;# Create a list of tool names for the tools provided
&lt;/span&gt;        &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_names&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;formatted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; 

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomOutputParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AgentOutputParser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AgentAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentFinish&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# Check if agent should finish
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Marathon Day&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AgentFinish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;return_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Marathon Day:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;marathon_date&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AgentFinish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="c1"&gt;# Return values is generally always a dictionary with a single `output` key
&lt;/span&gt;                &lt;span class="c1"&gt;# It is not recommended to try anything else at the moment :)
&lt;/span&gt;                &lt;span class="n"&gt;return_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Parse out the action and action input
&lt;/span&gt;        &lt;span class="n"&gt;regex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DOTALL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;action_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;# Return the action and action input
&lt;/span&gt;                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AgentAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;action_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm_output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Could not parse LLM output: `&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeprefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Could not parse LLM output: `&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;removesuffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That helps shape the output. Let's work on receiving the input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make a Streamlit web app in Python
&lt;/h3&gt;

&lt;p&gt;To make a webpage to receive input about the user for their personal marathon training plan, we will use Streamlit.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;request.py&lt;/em&gt;, we set a &lt;a href="https://docs.streamlit.io/library/api-reference/text/st.title" rel="noopener noreferrer"&gt;title&lt;/a&gt;, &lt;a href="https://docs.streamlit.io/library/api-reference/text/st.subheader" rel="noopener noreferrer"&gt;subheader&lt;/a&gt;, the dates that are first displayed when the &lt;a href="https://docs.streamlit.io/library/api-reference/widgets/st.date_input" rel="noopener noreferrer"&gt;date input widget is rendered&lt;/a&gt;, and a &lt;a href="https://docs.streamlit.io/library/api-reference/control-flow/st.form" rel="noopener noreferrer"&gt;Streamlit form&lt;/a&gt; containing two &lt;code&gt;text_input&lt;/code&gt; fields for the user's Strava token and email, make the date input widget, and then write the input dates to the page and split up the input dates into two variables. This code goes directly beneath &lt;code&gt;response = response.removeprefix("Could not parse LLM output: ").removesuffix("")&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Personal Marathon Training plan generator&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subheader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;enter details below&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;jul_30&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec_31&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_form&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;strava_token_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Strava API token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Email to send plan to&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Select your training dates for the year: it should be from the first training date to the marathon date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jul_30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;jul_30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;dec_31&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Training date: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Marathon date: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;training_start_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;marathon_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;submitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;form_submit_button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Submit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the command &lt;code&gt;streamlit run request.py&lt;/code&gt; in your terminal. An application should open up on your &lt;em&gt;localhost:8080&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0j7ijhdnygytp0lhyll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr0j7ijhdnygytp0lhyll.png" alt="Streamlit app running" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beneath the last line &lt;code&gt;marathon_date = dates[1]&lt;/code&gt;, add the following code to validate the user input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;submitted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;validate_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invalid email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🚨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;VALIDANSWERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;training_start_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Start date must be earlier than end date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🚨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;VALIDANSWERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;     
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://www.strava.com/api/v3/activities?access_token=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;strava_token_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;VALIDANSWERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check Strava token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;VALIDANSWERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;VALIDANSWERS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get Strava data into Pandas and CSV form
&lt;/h3&gt;

&lt;p&gt;The parameters and headers need to be set in order to call a Strava API request. &lt;code&gt;my_dataset&lt;/code&gt; contains data from just the first page of user activities, so to retrieve more activity data, loop through four pages of Strava activities and add them to &lt;code&gt;my_dataset&lt;/code&gt; before calling &lt;code&gt;pd.json_normalize&lt;/code&gt; on it to turn the semi-structured JSON data into a flat table to work with it.&lt;/p&gt;

&lt;p&gt;Directly beneath the &lt;code&gt;if VALIDANSWERS&lt;/code&gt;: line, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;strava_token_input&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
            &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;per_page&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;page&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;#max 200 per page, can only do 1 page at a time
&lt;/span&gt;            &lt;span class="n"&gt;my_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activities_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;#activities 1st page
&lt;/span&gt;            &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;#loop through 4 pages of strava activities
&lt;/span&gt;                &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; 
            &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;per_page&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;page&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;my_dataset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activities_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 

            &lt;span class="n"&gt;activities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json_normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_dataset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a new Pandas Dataframe object with specific columns containing the categories you want to consider: this tutorial is only looking at the activity name, type (bike ride, run, swim, tennis, etc), distance, moving time, total elevation gain, and start date.&lt;/p&gt;

&lt;p&gt;The following code goes beneath &lt;code&gt;activities = pd.json_normalize(my_dataset)&lt;/code&gt; and creates a CSV file of the activities data after removing items from 2021 so it only includes workouts from 2022 and 2023. You can replace this with another year! It then makes a CSV file named &lt;em&gt;data_files/runs.csv&lt;/em&gt; containing the runs and no other activity types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;moving_time&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;total_elevation_gain&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;start_date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;activities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;activities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;activities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;activities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;activities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;start_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YEAR-YOU-WANT-TO-IGNORE-WORKOUTS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
            &lt;span class="n"&gt;activities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data_files/activities.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;runs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;activities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;activities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Run&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data_files/runs.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, convert meters to miles and the moving time seconds to minutes and hours with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;data_run_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data_files/runs.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;data_activity_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data_files/activities.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;m_conv_factor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1609&lt;/span&gt;

            &lt;span class="n"&gt;data_run_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data_run_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;convert_to_miles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;data_activity_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data_activity_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;convert_to_miles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="c1"&gt;#convert moving time secs to mins, hours
&lt;/span&gt;            &lt;span class="n"&gt;data_run_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;moving_time&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data_run_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;moving_time&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt; &lt;span class="c1"&gt;#slices off 0 days from moving_time
&lt;/span&gt;            &lt;span class="n"&gt;data_run_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data_files/runs.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;data_activity_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data_files/activities.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, this data will be passed to a LangChain agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate a Personal Marathon Plan with LangChain Agents
&lt;/h3&gt;

&lt;p&gt;In the same &lt;em&gt;request.py&lt;/em&gt; file, make an array called &lt;code&gt;tools&lt;/code&gt; containing the tools the agent has access to. Each tool gets a name, function to run, and description. The description is optional, but recommended because it's used by an agent to determine tool use.&lt;/p&gt;

&lt;p&gt;Beneath creating the &lt;code&gt;data_activity_df&lt;/code&gt; variable, add the tools array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nc"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows in csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;num_rows_in_dataframe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;use to get the number of rows in csv file to calculate averages from running data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Calculator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm_math_chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;useful for when you need to answer questions about math&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;days_between&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;day1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc_days_btwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;useful for when you need to calculate the number of days between two dates of type date, like between the training start date and the marathon date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create some &lt;a href="https://python.langchain.com/docs/integrations/toolkits/pandas" rel="noopener noreferrer"&gt;Pandas dataframe agents&lt;/a&gt; to interact with the Strava data. These agents are similar to &lt;a href="https://python.langchain.com/docs/integrations/toolkits/csv" rel="noopener noreferrer"&gt;CSV agents&lt;/a&gt; which load data from CSV files instead of dataframes and perform queries. You could use both and see how the output differs.&lt;/p&gt;

&lt;p&gt;Agents can be chained together to build more complex applications. Another agent used below is of type &lt;code&gt;ZERO_SHOT_REACT_DESCRIPTION&lt;/code&gt;. Zero-shot means the agent functions only on the present action — it lacks memory. It uses the &lt;a href="https://ai.googleblog.com/2022/11/react-synergizing-reasoning-and-acting.html" rel="noopener noreferrer"&gt;ReAct framework&lt;/a&gt; to decide which tool to use, solely based on the tool’s description.&lt;/p&gt;

&lt;p&gt;The code runs the agents, telling them what to do and saving the output to variables for use later. Be specific with the prompts here and play around with different questions or commands.&lt;/p&gt;

&lt;p&gt;Beneath the tools array, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pd_agent_run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_pandas_dataframe_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;data_run_df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;pd_agent_activity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_pandas_dataframe_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;data_activity_df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initialize_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AgentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZERO_SHOT_REACT_DESCRIPTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;num_workouts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Calculate the number of days between these two dates representing the number of workouts in the plan: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; and &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;training_start_date&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. You have access to tools&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pd_run_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd_agent_run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Calculate average distance and maximum distance in miles and average moving time and maximum moving time in minutes. Additionally, calculate any other running statistics from the data you think would be helpful to consider in marathon training.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, make a prompt template, a "reproducible way to generate a prompt" containing a text string ("the template"). It accepts a set of parameters from the user and generates a prompt. This mirrors the items in &lt;em&gt;stapp.py&lt;/em&gt;'s &lt;code&gt;examples&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prefix&lt;/code&gt; contains directions and context to the LLM and &lt;code&gt;suffix&lt;/code&gt; is the user input and output indicator. Finally, these, along with the input variables which are unique to the student the plan is being made for, are passed into &lt;code&gt;FewShotPromptTemplate&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;coach_template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;training_start_date:{training_start_date}, marathon_date:{marathon_date}, pd_run_output: {pd_run_output}, num_workouts: {num_workouts}, plan: {plan}
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="n"&gt;example_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;training_start_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;marathon_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pd_run_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;num_workouts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;coach_template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            You are a marathon trainer tasked with crafting one personalized marathon training plan containing  according to your student&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s previous runs and activities.
            For each week you should recommend 2 cross-training activities and no more than one run greater than 14 miles per week. The longest run you recommend should be 20 miles around 2 weeks before {marathon_date}.
            There should be {num_workouts} workouts for {num_workouts} days, and there should be easy workouts and rest days in the week leading up to {marathon_date}.
            Slightly modify the following example marathon training plans based on your student and the days beginning on {training_start_date} up until {marathon_date} to create one personalized training plan with each workout on a new line: 
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
            training_start_date: {training_start_date}, marathon_date: {marathon_date}, pd_run_output: {pd_run_output}, num_workouts: {num_workouts}
            &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="n"&gt;few_shot_prompt_template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FewShotPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stapp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;example_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;example_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;input_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;marathon_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;training_start_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pd_run_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;num_workouts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a custom &lt;a href="https://python.langchain.com/docs/modules/model_io/output_parsers/" rel="noopener noreferrer"&gt;output parser&lt;/a&gt; object to help structure the response from the LLM and LLM chain, the most common type of chain. The LLM chain consists of the model and prompt. For this case it's an LLM but it could be a ChatModel. The LLM is passed to a &lt;code&gt;LLMSingleActionAgent&lt;/code&gt; to complete a single action and structure the response from the LLM.&lt;/p&gt;

&lt;p&gt;This agent is passed to an &lt;a href="https://docs.langchain.com/docs/components/agents/agent-executor" rel="noopener noreferrer"&gt;AgentExecutor&lt;/a&gt;. An executor differs from an agent in that it is a running instance of an agent used to execute tasks according to the agent’s decisions and configured tools. In short, agents decide which tools to use and the executors execute them. The output of the Executor (which is given the user input) is the personalized marathon training plan. Beneath the &lt;code&gt;FewShotPromptTemplate()&lt;/code&gt; function, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;output_parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CustomOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;#LLM chain consisting of the LLM and a prompt
&lt;/span&gt;&lt;span class="n"&gt;llm_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LLMChain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;few_shot_prompt_template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tool_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LLMSingleActionAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;llm_chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm_chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;output_parser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;output_parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\Marathon Day:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
    &lt;span class="n"&gt;allowed_tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tool_names&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agent_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_agent_and_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;marathon_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;training_start_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;training_start_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pd_run_output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pd_run_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;num_workouts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_workouts&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plan being print &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Email the Plan as a PDF using Twilio SendGrid
&lt;/h3&gt;

&lt;p&gt;We access the part of the plan containing the dates, replace new lines with breaks because the &lt;em&gt;ReportLab&lt;/em&gt; Python library to generate and format PDFs ignores &lt;code&gt;\n&lt;/code&gt;, and send an outbound email using Twilio SendGrid–the email body says "good luck at your marathon on {marathon_date}" with the plan attached.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;br /&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#each workout on new line
&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;from_email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;langchain_sendgrid_marathon_trainer@sf.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;to_emails&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Your AI-generated marathon training plan&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;html_content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;strong&amp;gt;Good luck at your marathon on %s&amp;lt;/strong&amp;gt;!&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Your plan is attached.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marathon_date&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The email attachment is formatted and styled using &lt;em&gt;ReportLab&lt;/em&gt;. The following code returns a stylesheet with a few basic heading and text styles to format the PDF attachment which is in the form of a SimpleDocTemplate document. The document is built using the Paragraph class that lets us format the text with inline font style (and optional color changes) using an XML style markup.&lt;/p&gt;

&lt;p&gt;The encoded file is passed as an attachment to the SendGrid email. If the email is sent successfully, the Streamlit app displays a success message; otherwise, it displays an error message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;styleN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSampleStyleSheet&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Normal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

            &lt;span class="n"&gt;pdf_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plan.pdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleDocTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;pdf_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;pagesize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;bottomMargin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;topMargin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;rightMargin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;leftMargin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styleN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;encoded_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="n"&gt;attachedFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;FileContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded_file&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;FileName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;personal_ai_generated_marathon_training_plan.pdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;FileType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/pdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Disposition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attachedFile&lt;/span&gt;
            &lt;span class="n"&gt;sg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SendGridAPIClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;202&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Email sent! Check your email for your personal training plan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Response Code: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; Message sent!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Email not sent--check email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;streamlit run request.py&lt;/code&gt; in your terminal. Fill in your information (like Strava Access Token, the email where you want to receive the generated plan, the training start date, and marathon race date!) on the Streamlit app, click &lt;strong&gt;Submit&lt;/strong&gt;, and wait for your personal marathon training plan in your email.&lt;/p&gt;

&lt;p&gt;If the &lt;strong&gt;Submit&lt;/strong&gt; button is clicked, we check the input (dates and email). If the inputs are either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invalid: the Streamlit app displays an error message and caches the function so the user can try valid input.&lt;/li&gt;
&lt;li&gt;valid: the Streamlit app displays a success message and it's time to use the user's Strava API token to get their Strava workout data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuvr8yyz5ncq9aqf5d47.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuvr8yyz5ncq9aqf5d47.png" alt="Image description" width="800" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Play around with different agents, prompting, and different parameters like temperature (which affects the risks the agent takes and creativity of the output).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/elizabethsiegle/ai-generated-marathon-trainer/blob/main/request.py" rel="noopener noreferrer"&gt;complete code above can be found here on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for Agents and SendGrid
&lt;/h3&gt;

&lt;p&gt;So will I use this to train for my marathon? I haven't coded this much since college (or had this much fun doing so) and this was a great way to learn many of LangChain's features, but time will tell –work travel, tennis practices, and &lt;a href="https://partiful.com/e/ac7NHwQ8ZLVIrkSHoVLP" rel="noopener noreferrer"&gt;AI hackathons&lt;/a&gt; make consistent training difficult. I do want to continue trying different ways of changing the output--this is a lot of code which I feel like could be unnecessary, but it yielded the best results.&lt;/p&gt;

&lt;p&gt;There's so much developers can do with communication APIs and AI–this is just the tip of the iceberg. You can use a different LLM, look at different Strava data, try different agents (or use chains instead!), embeddings, use &lt;a href="https://replicate.com/" rel="noopener noreferrer"&gt;LLaMA 2 or other similar models on Replicate&lt;/a&gt;, and more. You could use SMS or &lt;a href="https://www.twilio.com/docs/conversations" rel="noopener noreferrer"&gt;Twilio Conversations&lt;/a&gt; to collect user input instead of Streamlit, or make the LLM recommend other workouts for different goals or meals based on nutrition data!&lt;/p&gt;

&lt;p&gt;Let me know online what you're building (and send running tips!)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter: &lt;a href="http://twitter.com/lizziepika" rel="noopener noreferrer"&gt;@lizziepika&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="http://github.com/elizabethsiegle" rel="noopener noreferrer"&gt;elizabethsiegle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:lsiegle@twilio.com"&gt;lsiegle@twilio.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>twilio</category>
      <category>sendgrid</category>
    </item>
    <item>
      <title>Build an AI SMS Chatbot with Replicate, LLaMA 2, and LangChain</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Tue, 12 Sep 2023 22:16:09 +0000</pubDate>
      <link>https://dev.to/twilio/build-an-ai-sms-chatbot-with-replicate-llama-2-and-langchain-3i72</link>
      <guid>https://dev.to/twilio/build-an-ai-sms-chatbot-with-replicate-llama-2-and-langchain-3i72</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/ai-sms-chatbot-replicate-llama-2-langchain" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently, Meta and Microsoft introduced the second generation of the LLaMA LLM (Large Language Model) to help developers and organizations to build generative AI-powered tools and experiences. Read on to learn how to build an AI SMS chatbot that answers questions like Ahsoka (from Star Wars) using LangChain templating, LLaMa 2, Replicate, and Twilio Programmable Messaging!&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc11hzs7x148okb1zjlj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc11hzs7x148okb1zjlj3.png" alt="SMS example" width="800" height="196"&gt;&lt;/a&gt;&lt;br&gt;
Do you prefer learning via video more? Check out &lt;a href="https://www.tiktok.com/@lizziepikachu/video/7278020285750889774" rel="noopener noreferrer"&gt;this TikTok summarizing this tutorial&lt;/a&gt; in 1 minute!&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A Twilio account - &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up for a free Twilio account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Twilio phone number with SMS capabilities - &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;learn how to buy a Twilio Phone Number here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Replicate account to host the LlaMA 2 model – &lt;a href="https://replicate.com/signin?next=/" rel="noopener noreferrer"&gt;make a Replicate account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python installed - &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;download Python here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, a handy utility to connect the development version of our Python application running on your machine to a public URL that Twilio can access.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ &lt;strong&gt;ngrok is needed for the development version of the application because your computer is likely behind a router or firewall, so it isn’t directly reachable on the Internet. You can also choose to automate ngrok as shown in this article.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Replicate
&lt;/h3&gt;

&lt;p&gt;Replicate offers a cloud API and tools so you can more easily run machine learning models, abstracting away some lower-level machine learning concepts and handling infrastructure so you can focus more on your own applications. You can run open-source models that others have published, or package and publish your own, either publicly or privately.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Since you will be installing some Python packages for this project, you will need to make a new project directory and a &lt;a href="https://docs.python.org/3/tutorial/venv.html" rel="noopener noreferrer"&gt;virtual environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're using a Unix or macOS system, open a terminal and enter the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;replicate-llama-ai-sms-chatbot  
&lt;span class="nb"&gt;cd &lt;/span&gt;replicate-llama-ai-sms-chatbot  
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate 
pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain replicate flask twilio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're following this tutorial on Windows, enter the following commands in a command prompt window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;replicate-llama-ai-sms-chatbot  
&lt;span class="nb"&gt;cd &lt;/span&gt;replicate-llama-ai-sms-chatbot   
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate 
pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain replicate flask twilio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://replicate.com/account/api-tokens" rel="noopener noreferrer"&gt;Grab your default Replicate API Token or create a new one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faat6zydxbdnq8sohcc4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faat6zydxbdnq8sohcc4x.png" alt="Replicate console" width="800" height="225"&gt;&lt;/a&gt;&lt;br&gt;
On the command line run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REPLICATE_API_TOKEN&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;replace with your api token&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's time to write some code!&lt;/p&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;

&lt;p&gt;Make a file called &lt;em&gt;app.py&lt;/em&gt; and place the following import statements at the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMChain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.llms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Replicate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.memory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConversationBufferWindowMemory&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twilio.twiml.messaging_response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessagingResponse&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Though LLaMA 2 is tuned for chat, templates are still helpful so the LLM knows what behavior is expected of it. This starting prompt is similar to ChatGPT so it should behave similarly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Assistant is a large language model.

Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.

Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.

Overall, Assistant is a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist. 

I want you to act as Ahsoka giving advice and answering questions. You will reply with what she would say.
SMS: {sms_input}
Assistant:&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sms_input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, make a LLM Chain, one of the core components of LangChain. This allows us to chain together prompts and make a prompt history. The model is formatted as the model name followed by the version–in this case, the model is LlaMA 2, a 13-billion parameter language model from Meta fine-tuned for chat completions. &lt;code&gt;max_length&lt;/code&gt; is 4096, the maximum number of tokens (called the &lt;em&gt;context window&lt;/em&gt;) the LLM can accept as input when generating responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sms_chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LLMChain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Replicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a16z-infra/llama13b-v2-chat:df7690f1994d94e96ad9d568eac121aecf50684a0b0963b25a41cc40061269e5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ConversationBufferWindowMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;llm_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, make a Flask app to accept inbound text messages, pass that to the LLM Chain, and return the output as an outbound text message with Twilio Programmable Messaging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/sms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;inb_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sms_chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sms_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inb_msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the command line, run &lt;code&gt;python app.py&lt;/code&gt; to start the Flask app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure a Twilio Number for the SMS Chatbot
&lt;/h3&gt;

&lt;p&gt;Now, your Flask app will need to be visible from the web so Twilio can send requests to it. ngrok lets you do this. With ngrok installed, run &lt;code&gt;ngrok http 5000&lt;/code&gt; in a new terminal tab in the directory your code is in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kgwrxcxdfgo9tkrp42o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kgwrxcxdfgo9tkrp42o.png" alt="ngrok terminal tab" width="800" height="334"&gt;&lt;/a&gt;&lt;br&gt;
You should see the screen above. Grab that ngrok &lt;strong&gt;Forwarding URL&lt;/strong&gt; to configure your Twilio number: select your Twilio number under &lt;strong&gt;Active Numbers&lt;/strong&gt; in your &lt;a href="https://www.twilio.com/console/phone-numbers/incoming" rel="noopener noreferrer"&gt;Twilio console&lt;/a&gt;, scroll to the &lt;strong&gt;Messaging&lt;/strong&gt; section, and then modify the phone number’s routing by pasting the ngrok URL with the &lt;em&gt;/sms&lt;/em&gt; path in the textbox corresponding to when &lt;strong&gt;A Message Comes In&lt;/strong&gt; as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6lgkzfqevilybi3al5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6lgkzfqevilybi3al5s.png" alt="configure phone number" width="800" height="335"&gt;&lt;/a&gt;&lt;br&gt;
Click &lt;strong&gt;Save&lt;/strong&gt; and now your Twilio phone number is configured so that it maps to your web application server running locally on your computer and your application can run. Text your Twilio number a question relating to the text file and get an answer from that file over SMS!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xi0flyo9fdgq4okmwgd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xi0flyo9fdgq4okmwgd.png" alt="SMS example" width="800" height="226"&gt;&lt;/a&gt;&lt;br&gt;
You can view the &lt;a href="https://github.com/elizabethsiegle/replicate-llama2-sms-chatbot" rel="noopener noreferrer"&gt;complete code on GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for Twilio, LangChain, Replicate, and LLaMA 2?
&lt;/h3&gt;

&lt;p&gt;There is so much fun for developers to have around building with LLMs! You can modify existing LangChain and LLM projects to use LLaMA 2 instead of GPT, build a web interface using &lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt; instead of SMS, fine-tune LLaMA 2 with your own data, and more! I can't wait to see what you build–let me know online what you're working on!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter: &lt;a href="https://twitter.com/lizziepika" rel="noopener noreferrer"&gt;@lizziepika&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/elizabethsiegle" rel="noopener noreferrer"&gt;elizabethsiegle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:lsiegle@twilio.com"&gt;lsiegle@twilio.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>replicate</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Build a SMS Bot that Answers questions over Docs with LangChain in Python</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Thu, 15 Jun 2023 20:33:43 +0000</pubDate>
      <link>https://dev.to/twilio/build-a-sms-bot-that-answers-questions-over-docs-with-langchain-in-python-47fj</link>
      <guid>https://dev.to/twilio/build-a-sms-bot-that-answers-questions-over-docs-with-langchain-in-python-47fj</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/qa-over-docs-bot-langchain-python" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://www.twilio.com/docs/glossary/what-natural-language-processing-nlp" rel="noopener noreferrer"&gt;Natural Language Processing (NLP)&lt;/a&gt;, you can chat with your own documents, such as a text file, a PDF, or a website. Read on to learn how to build a generative question-answering SMS chatbot that reads a document containing &lt;a href="https://www.si.com/mlb/2009/07/05/gehrig-text" rel="noopener noreferrer"&gt;Lou Gehrig's Farewell Speech&lt;/a&gt; using &lt;a href="https://python.langchain.com/en/latest/index.html" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;, &lt;a href="https://huggingface.co/" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;, and Twilio in Python.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AllYZ-qU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/Screenshot_2023-06-15_at_11.44.44_AM.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AllYZ-qU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/Screenshot_2023-06-15_at_11.44.44_AM.format-webp.webp" alt="sms example where I text what would Lou Gehrig give to beat the New York Giants and the bot replies with he would give his right arm to beat them" width="492" height="819"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  LangChain Q&amp;amp;A
&lt;/h2&gt;

&lt;p&gt;LangChain is an open-source tool that wraps around many large language models (LLMs) and tools. It is the easiest way (if not one of the easiest ways) to interact with LLMs and build applications around LLMs.&lt;/p&gt;

&lt;p&gt;LangChain makes it easy to perform question-answering of those documents. Picture feeding a PDF or maybe multiple PDF files to a machine and then asking it questions about those files. This could be useful, for example, if you have to prepare for a test and wish to ask the machine about things you didn’t understand.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A Twilio account - &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up for a free Twilio account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Twilio phone number with SMS capabilities - &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;learn how to buy a Twilio Phone Number here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hugging Face Account – &lt;a href="https://huggingface.co/join" rel="noopener noreferrer"&gt;make a Hugging Face Account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python installed - &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;download Python here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, a handy utility to connect the development version of our Python application running on your machine to a public URL that Twilio can access.
&amp;gt; ⚠️ &lt;strong&gt;ngrok is needed for the development version of the application because your computer is likely behind a router or firewall, so it isn’t directly reachable on the Internet. You can also &lt;a href="https://www.twilio.com/blog/automating-ngrok-python-twilio-applications-pyngrok" rel="noopener noreferrer"&gt;choose to automate ngrok as shown in this article&lt;/a&gt;.&lt;/strong&gt;: 
## Configuration
Since you will be installing some Python packages for this project, you will need to make a new project directory and a virtual environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're using a Unix or macOS system, open a terminal and enter the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;lc-qa-sms 
&lt;span class="nb"&gt;cd &lt;/span&gt;lc-qa-sms 
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate 
&lt;span class="o"&gt;!&lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain
&lt;span class="o"&gt;!&lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
pip &lt;span class="nb"&gt;install &lt;/span&gt;faiss-cpu
pip &lt;span class="nb"&gt;install &lt;/span&gt;sentence-transformers
pip &lt;span class="nb"&gt;install &lt;/span&gt;twilio
pip &lt;span class="nb"&gt;install &lt;/span&gt;load_dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're following this tutorial on Windows, enter the following commands in a command prompt window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;lc-qa-sms  
&lt;span class="nb"&gt;cd &lt;/span&gt;lc-qa-sms 
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate 
pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain
pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
pip &lt;span class="nb"&gt;install &lt;/span&gt;faiss
pip &lt;span class="nb"&gt;install &lt;/span&gt;sentence-transformers
pip &lt;span class="nb"&gt;install &lt;/span&gt;twilio
pip &lt;span class="nb"&gt;install &lt;/span&gt;load_dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up Hugging Face Hub
&lt;/h2&gt;

&lt;p&gt;The Hugging Face Hub offers over 120k models, 20k datasets, and 50k demos people can easily collaborate in their ML workflows. As mentioned earlier, this project needs a Hugging Face Hub Access Token to use the LangChain endpoints to a Hugging Face Hub LLM. After making a Hugging Face account, you can &lt;a href="https://huggingface.co/settings/tokens" rel="noopener noreferrer"&gt;get a Hugging Face Access Token here&lt;/a&gt; by clicking on &lt;strong&gt;New token&lt;/strong&gt;. Give the token a name and select the &lt;strong&gt;Read&lt;/strong&gt; Role.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y6yNR0Qd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/huggingfacehub.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y6yNR0Qd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/huggingfacehub.format-webp.webp" alt="hugging face hub with access tokens page" width="800" height="412"&gt;&lt;/a&gt;&lt;br&gt;
Alternatively, you could use models from, say, OpenAI.&lt;/p&gt;

&lt;p&gt;On the command line in your root directory, run the following command on a Mac to set the token as an environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HUGGINGFACEHUB_API_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;replace-with-your-huggingfacehub-token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Windows command, &lt;a href="https://www.twilio.com/blog/how-to-set-environment-variables-html" rel="noopener noreferrer"&gt;check out this blog post on environment variables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now let's build that LangChain question-answering chatbot application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Answer Questions from a Doc with LangChain via SMS
&lt;/h2&gt;

&lt;p&gt;Inside your &lt;code&gt;lc-qa-sms&lt;/code&gt; directory, make a new file called &lt;em&gt;app.py&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;At the top of the file, add the following lines to import the required libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.document_loaders&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TextLoader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.text_splitter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CharacterTextSplitter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.embeddings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HuggingFaceEmbeddings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.vectorstores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chains.question_answering&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_qa_chain&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HuggingFaceHub&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twilio.twiml.messaging_response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessagingResponse&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beneath those import statements, make a helper function to write to a local file from a URL, and then load that file from the local file storage with LangChain's &lt;code&gt;TextLoader&lt;/code&gt; library. Later the file will be passed over to the &lt;code&gt;split&lt;/code&gt; method to create chunks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loadFileFromURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_file_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;output_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lougehrig.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_file_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# load text doc from URL w/ TextLoader
&lt;/span&gt;    &lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TextLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;txt_file_as_loaded_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;txt_file_as_loaded_docs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this tutorial, the file will be Lou Gehrig's famous speech, which is why the &lt;code&gt;output_file&lt;/code&gt; is named "lougehrig.txt".&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NwH1hyJq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/lougehrigfarewell.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NwH1hyJq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/lougehrigfarewell.format-webp.webp" alt="lou gehrig farewell speech on field image" width="299" height="168"&gt;&lt;/a&gt;&lt;br&gt;
You could alternatively use a local file.&lt;/p&gt;

&lt;p&gt;Next, add the following code for a helper function to split the document into chunks. This is important because LLMs can't process inputs that are too long. LangChain's &lt;code&gt;CharacterTextSplitter&lt;/code&gt; function helps us do this–setting &lt;code&gt;chunk_size&lt;/code&gt; to 1000 and &lt;code&gt;chunk_overlap&lt;/code&gt; to 10 keeps the integrity of the file by avoiding splitting words in half.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;splitDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loaded_docs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# split docs into chunks
&lt;/span&gt;    &lt;span class="n"&gt;splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;chunked_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loaded_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chunked_docs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now convert the chunked document into embeddings (numerical representations of words) with Hugging Face and store them in a FAISS Vector Store. &lt;a href="https://faiss.ai/" rel="noopener noreferrer"&gt;Faiss&lt;/a&gt; is a library for efficient similarity search and clustering of dense vectors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;makeEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunked_docs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Create embeddings and store them in a FAISS vector store
&lt;/span&gt;    &lt;span class="n"&gt;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HuggingFaceEmbeddings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunked_docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;makeEmbeddings&lt;/code&gt; function will make it more efficient to retrieve and manipulate the stored embeddings to conduct a similarity search to get the most semantically-similar documents to a given input which the LLM needs to best answer questions in the following helper function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;askQs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Ask a question using the QA chain
&lt;/span&gt;    &lt;span class="n"&gt;similar_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;similarity_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_documents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;similar_docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final helper function defines and loads the Hugging Face Hub LLM to be used with your Access Token and starts the request on a similarity search embedded with some input question to the selected LLM, enabling a question-and-answer conversation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loadLLM&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;HuggingFaceHub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;declare-lab/flan-alpaca-large&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_qa_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stuff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;declare-lab/flan-alpaca-large is a LLM. You could use others from Hugging Face Hub such as google-flan-t5-xl. They are trained on different corpuses and this tutorial uses flan-alpaca-large because it's faster.&lt;/p&gt;

&lt;p&gt;Lastly, make a Flask app using the Twilio REST API to call the helper functions and respond to inbound text messages with information pulled from the text file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/sms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;inb_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;#get inbound text body
&lt;/span&gt;    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadLLM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;LOCAL_ldocs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadFileFromURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/elizabethsiegle/qanda-langchain-sms-lougehrig/main/lougehrig.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOCAL_cdocs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;splitDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_ldocs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#chunked
&lt;/span&gt;    &lt;span class="n"&gt;LOCAL_vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_cdocs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOCAL_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;askQs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_vector_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inb_msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your complete &lt;code&gt;app.py&lt;/code&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.document_loaders&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TextLoader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.text_splitter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CharacterTextSplitter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.embeddings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HuggingFaceEmbeddings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.vectorstores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chains.question_answering&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_qa_chain&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HuggingFaceHub&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twilio.twiml.messaging_response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessagingResponse&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loadFileFromURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_file_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;#param: https://raw.githubusercontent.com/elizabethsiegle/qanda-langchain-sms-lougehrig/main/lougehrig.txt
&lt;/span&gt;    &lt;span class="n"&gt;output_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lougehrig.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_file_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# load text doc from URL w/ TextLoader
&lt;/span&gt;    &lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TextLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;txt_file_as_loaded_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;txt_file_as_loaded_docs&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;splitDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loaded_docs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# split docs into chunks
&lt;/span&gt;    &lt;span class="n"&gt;splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;chunked_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loaded_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chunked_docs&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;makeEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunked_docs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Create embeddings and store them in a FAISS vector store
&lt;/span&gt;    &lt;span class="n"&gt;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HuggingFaceEmbeddings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunked_docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;askQs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Ask a question using the QA chain
&lt;/span&gt;    &lt;span class="n"&gt;similar_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;similarity_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_documents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;similar_docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loadLLM&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;HuggingFaceHub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;declare-lab/flan-alpaca-large&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_qa_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stuff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/sms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;inb_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;#get inbound text body
&lt;/span&gt;    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadLLM&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;LOCAL_ldocs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadFileFromURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/elizabethsiegle/qanda-langchain-sms-lougehrig/main/lougehrig.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOCAL_cdocs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;splitDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_ldocs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#chunked
&lt;/span&gt;    &lt;span class="n"&gt;LOCAL_vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_cdocs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOCAL_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;askQs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_vector_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inb_msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the command line, run &lt;code&gt;python app.py&lt;/code&gt; to start the Flask app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure a Twilio Number for the SMS Chatbot
&lt;/h2&gt;

&lt;p&gt;Now, your Flask app will need to be visible from the web so Twilio can send requests to it. ngrok lets you do this. With ngrok installed, run &lt;code&gt;ngrok http 5000&lt;/code&gt; in a new terminal tab in the directory your code is in.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eQui8b3S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/ngrok_zGN7AVJ.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eQui8b3S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/ngrok_zGN7AVJ.format-webp.webp" alt="ngrok http 5000 terminal with forwarding URLs opening up your local server to the web" width="800" height="334"&gt;&lt;/a&gt;&lt;br&gt;
You should see the screen above. Grab that ngrok &lt;strong&gt;Forwarding URL&lt;/strong&gt; to configure your Twilio number: select your Twilio number under &lt;strong&gt;Active Numbers &lt;/strong&gt;in your Twilio console, scroll to the Messaging section, and then modify the phone number’s routing by pasting the ngrok URL with the &lt;em&gt;/sms&lt;/em&gt; path in the textbox corresponding to when &lt;strong&gt;A Message Comes In&lt;/strong&gt; as shown below:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4tuK9I6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/twiliosmsconfigphone.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4tuK9I6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/twiliosmsconfigphone.format-webp.webp" alt="configure twilio phone number webpage with ngrok url when a message comes in" width="800" height="335"&gt;&lt;/a&gt;&lt;br&gt;
Click &lt;strong&gt;Save&lt;/strong&gt; and now your Twilio phone number is configured so that it maps to your web application server running locally on your computer and your application can run. Text your Twilio number a question relating to the text file and get an answer from that file over SMS!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XP2oEAOq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/Screenshot_2023-06-15_at_11.49.59_AM.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XP2oEAOq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/Screenshot_2023-06-15_at_11.49.59_AM.format-webp.webp" alt="sms example where i text what kind of break was lou given and the bot/llm responds lou was given a bad break" width="800" height="734"&gt;&lt;/a&gt;&lt;br&gt;
The &lt;a href="https://github.com/elizabethsiegle/qanda-langchain-sms-lougehrig" rel="noopener noreferrer"&gt;complete code can be found here on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next for Twilio and LangChain
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mGPg0Dwh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/langchainlogo.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mGPg0Dwh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/langchainlogo.format-webp.webp" alt="langchain logo" width="453" height="111"&gt;&lt;/a&gt;&lt;br&gt;
There's so much you can do with document-based question-answering. You can use a different LLM, use a longer document than a text file containing Lou Gehrig's famous speech, use other types of documents like a PDF or website (&lt;a href="https://python.langchain.com/en/latest/modules/indexes/document_loaders.html" rel="noopener noreferrer"&gt;here's LangChain's docs on documents&lt;/a&gt;), store the embeddings elsewhere, and more. Other than a SMS chatbot, you could create an AI tutor, search engine, automated customer service agent, and more.&lt;/p&gt;

&lt;p&gt;Let me know online what you're building!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter: &lt;a href="http://twitter.com/lizziepika" rel="noopener noreferrer"&gt;@lizziepika&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="http://github.com/elizabethsiegle" rel="noopener noreferrer"&gt;elizabethsiegle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:lsiegle@twilio.com"&gt;lsiegle@twilio.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>langchain</category>
      <category>ai</category>
      <category>nlp</category>
    </item>
    <item>
      <title>Build a Basketball SMS Chatbot with LangChain Prompt Templates in Python</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Wed, 31 May 2023 22:00:33 +0000</pubDate>
      <link>https://dev.to/twilio/build-a-basketball-sms-chatbot-with-langchain-prompt-templates-in-python-5dld</link>
      <guid>https://dev.to/twilio/build-a-basketball-sms-chatbot-with-langchain-prompt-templates-in-python-5dld</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/basketball-sms-chatbot-with-langchain-prompt-templates" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As I've played with the &lt;a href="https://www.twilio.com/blog/sms-chatbot-openai-api-node" rel="noopener noreferrer"&gt;OpenAI API&lt;/a&gt; (and &lt;a href="https://www.twilio.com/blog/generate-art-with-dall-e-2-and-serverless-sms" rel="noopener noreferrer"&gt;DALL·E&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/finetune-openai-ml-model-node" rel="noopener noreferrer"&gt;fine-tuning models&lt;/a&gt;) and gotten more into &lt;a href="https://www.twilio.com/docs/glossary/what-is-machine-learning" rel="noopener noreferrer"&gt;ML&lt;/a&gt;, developers I meet (as well as my &lt;a href="https://twitter.com/craigsdennis" rel="noopener noreferrer"&gt;wonderful coworker Craig Dennis&lt;/a&gt; who advised me on this tutorial) keep telling me to use LangChain, a powerful and flexible framework for developing applications powered by language models.&lt;/p&gt;

&lt;p&gt;Read on to learn how to build an SMS chatbot using LangChain prompt templates, OpenAI, &lt;a href="https://www.twilio.com/docs/sms" rel="noopener noreferrer"&gt;Twilio Programmable Messaging&lt;/a&gt;, and Python.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FMXvI7M2ZP_Pu6X0ggT8XOPin8vHiCgiQibdTg1kCkou.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FMXvI7M2ZP_Pu6X0ggT8XOPin8vHiCgiQibdTg1kCkou.format-webp.webp" alt="sms example asking for a haiku about summer, who the greatest nba finals winners are, how many times Steph Curry has been to the nba finals with answers"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Large Language Models
&lt;/h2&gt;

&lt;p&gt;Large Language Models are trained on large quantities of textual data (i.e. in ChatGPT's case, the entire internet up to 2021–so you can provide more context or data that the model is missing with prompt engineering and Prompt Templates–more on those later) to produce human-like responses to dialogue or other natural language inputs.&lt;/p&gt;

&lt;p&gt;To yield these natural language responses, LLMs use deep learning (DL) models, which use multi-layered neural networks to process, analyze, and make predictions with complex data.&lt;/p&gt;
&lt;h2&gt;
  
  
  LangChain
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FCLK0PzsrPZu0uIGxgMbZ8V7HtCJC4myfgeCpb5fOLxU.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FCLK0PzsrPZu0uIGxgMbZ8V7HtCJC4myfgeCpb5fOLxU.format-webp.webp" alt="LangChain logo image"&gt;&lt;/a&gt;&lt;br&gt;
LangChain came out late last year and already has over 43,000 stars on GitHub and a thriving community of contributors.&lt;/p&gt;

&lt;p&gt;It is many things, but ultimately at its core is an open-source framework that simplifies the development of applications using large language models (LLMs), like OpenAI or Hugging Face. Developers can use it for chatbots, Generative Question-Answering (GQA), summarization, and more.&lt;/p&gt;

&lt;p&gt;With LangChain, developers can “chain” together different LLM components to create more advanced use cases around LLMs. Chains can consist of multiple components from several modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt Templates: Prompt templates are templates for different types of prompts. Like “chatbot” style templates, &lt;a href="https://paperswithcode.com/dataset/eli5" rel="noopener noreferrer"&gt;ELI5 question-answering&lt;/a&gt;, etc&lt;/li&gt;
&lt;li&gt;LLMs: Large language models like GPT-3, Hugging Face, &lt;a href="https://huggingface.co/bigscience/bloom" rel="noopener noreferrer"&gt;BLOOM&lt;/a&gt;, etc&lt;/li&gt;
&lt;li&gt;Agents: Agents use LLMs to decide what actions should be taken. Tools like web search or calculators can be used, and all are packaged into a logical loop of operations.&lt;/li&gt;
&lt;li&gt;Memory: Short-term memory, long-term memory.
LangChain recognizes the power of prompts and has built an entire set of objects for them. It also provides a wrapper around different LLMs so you can easily change models, swapping them out with different templates. The chat model could be different, but running and calling it is the same–a very Java-like concept!
## LangChain Twilio Tool
Recently, LangChain came out with a &lt;a href="https://python.langchain.com/en/latest/modules/agents/tools/examples/twilio.html" rel="noopener noreferrer"&gt;Twilio tool&lt;/a&gt; so your LangChain Agents are able to send text messages. For example, your LLM can understand the input in natural language, but Agents can let you complete different tasks like calling an API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll need your own Twilio credentials and to install it with &lt;code&gt;pip install twilio&lt;/code&gt;. The code for the tool would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.utilities.twilio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TwilioAPIWrapper&lt;/span&gt;

&lt;span class="n"&gt;twilio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TwilioAPIWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="n"&gt;account_sid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR-ACCOUNT-SID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR-TWILIO-AUTH-TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;from_number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR-TWILIO-NUMBER,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;​​&lt;/span&gt;&lt;span class="n"&gt;twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NUMBER-TO-TEXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tutorial will, however, show you how to use LangChain Prompt Templates with Twilio to make a SMS chatbot.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangChain Prompt Templates
&lt;/h2&gt;

&lt;p&gt;"Prompts” refer to the input to the model and are usually not hard-coded, but are more often constructed from multiple components. A &lt;a href="https://python.langchain.com/en/latest/modules/prompts/prompt_templates.html" rel="noopener noreferrer"&gt;Prompt Template&lt;/a&gt; helps construct this input. LangChain provides several classes and functions to make constructing and working with prompts easy.&lt;/p&gt;

&lt;p&gt;Prompts being input to LLMs are often structured in different ways so that we can get different results. For Q&amp;amp;A, you could take a user’s question and reformat it for different Q&amp;amp;A styles, like conventional Q&amp;amp;A, a bullet list of answers, or even a summary of problems relevant to the given question. You can read more about &lt;a href="https://python.langchain.com/en/latest/modules/prompts.html" rel="noopener noreferrer"&gt;prompts here in the LangChain documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Prompt templates offer a reproducible way to generate a prompt. Like a reusable HTML template, you can share, test, reuse, and iterate on it, and it will update. When you update a prompt template, it updates for anyone else using it or the whole set of apps that could use it.&lt;/p&gt;

&lt;p&gt;Prompt templates contain a text string (AKA “the template”) that can take in a set of parameters from the user and generate a prompt.&lt;/p&gt;

&lt;p&gt;Importing and initializing a LangChain &lt;code&gt;PromptTemplate&lt;/code&gt; class would look like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;

&lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Question: {question}

Answer: &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# user question
&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which NBA team won the finals in 1996?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since OpenAI LLMs lack data after September 2021, its models can't answer anything that occurred after without additional context. Prompt templates help provide additional context, but differ from fine-tuning - fine-tuning is like coaching the model with new data to get certain output, but prompt engineering with Prompt Templates provides the model specific data to help it get the output you want.&lt;/p&gt;

&lt;p&gt;Let's use this template to build the chatbot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A Twilio account - &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up for a free one here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Twilio phone number with SMS capabilities - &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;learn how to buy a Twilio Phone Number here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenAI Account – &lt;a href="https://openai.com/api/" rel="noopener noreferrer"&gt;make an OpenAI Account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python installed - &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;download Python here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, a handy utility to connect the development version of our Python application running on your machine to a public URL that Twilio can access.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;ngrok is needed for the development version of the application because your computer is likely behind a router or firewall, so it isn’t directly reachable on the Internet. You can also choose to automate ngrok as shown in this article.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Since you will be installing some Python packages for this project, you will need to make a new project directory and a &lt;a href="https://docs.python.org/3/tutorial/venv.html" rel="noopener noreferrer"&gt;virtual environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're using a Unix or macOS system, open a terminal and enter the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;lc-sms 
&lt;span class="nb"&gt;cd &lt;/span&gt;lc-sms 
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate 
&lt;span class="o"&gt;!&lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain
&lt;span class="o"&gt;!&lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;openai
pip &lt;span class="nb"&gt;install &lt;/span&gt;Flask
pip &lt;span class="nb"&gt;install &lt;/span&gt;twilio
pip &lt;span class="nb"&gt;install &lt;/span&gt;load_dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're following this tutorial on Windows, enter the following commands in a command prompt window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;lc-sms 
&lt;span class="nb"&gt;cd &lt;/span&gt;lc-sms 
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv 
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate 
pip &lt;span class="nb"&gt;install &lt;/span&gt;langchain
pip &lt;span class="nb"&gt;install &lt;/span&gt;openai
pip &lt;span class="nb"&gt;install &lt;/span&gt;Flask
pip &lt;span class="nb"&gt;install &lt;/span&gt;twilio
pip &lt;span class="nb"&gt;install &lt;/span&gt;load_dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last command uses &lt;code&gt;pip&lt;/code&gt;, the Python package installer, to install the three packages that you are going to use in this project, which are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://pypi.org/project/openai/" rel="noopener noreferrer"&gt;OpenAI Python client library&lt;/a&gt;, to send requests to OpenAI's &lt;a href="https://www.fullstackpython.com/gpt-3.html" rel="noopener noreferrer"&gt;GPT-3 engine&lt;/a&gt;. You could also use a &lt;a href="https://python.langchain.com/en/latest/modules/models.html" rel="noopener noreferrer"&gt;different LLM, listed here in the LangChain docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.twilio.com/docs/libraries/python" rel="noopener noreferrer"&gt;Twilio Python Helper library&lt;/a&gt;, to work with SMS messages.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.palletsprojects.com/p/flask/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; framework, to create the web application in Python.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://pypi.org/project/load-dotenv/" rel="noopener noreferrer"&gt;load_dotenv&lt;/a&gt; library to load environment variables from a .env file&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://pypi.org/project/langchain/" rel="noopener noreferrer"&gt;LangChain library&lt;/a&gt; to building applications with LLMs through composability
As mentioned above, this project needs an OpenAI API Key to use the LangChain endpoints to OpenAI. After making an OpenAI account, you can &lt;a href="https://beta.openai.com/account/api-keys" rel="noopener noreferrer"&gt;get an OpenAI API Key here&lt;/a&gt; by clicking on &lt;strong&gt;+ Create new secret key&lt;/strong&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2Fp9qM9s9XJkaoujt9jKFFUaunhZ8G3v-WrNBwg35b769.format-webp.webp" alt="openAI API key page"&gt;
The Python application will need to have access to this key, so we are going to make a &lt;em&gt;.env&lt;/em&gt; file where the API key can safely be stored. The application we create will be able to import this key as an environment variable soon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a &lt;em&gt;.env&lt;/em&gt; file in your project’s root directory and enter the following line of text, making sure to replace &lt;code&gt;&amp;lt;OPENAI_API_KEY&amp;gt;&lt;/code&gt; with your actual key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &amp;lt;YOUR-OPENAI-KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that the OPENAI_API_KEY is safe and that you don't expose your &lt;em&gt;.env&lt;/em&gt; file in a public location such as GitHub.&lt;/p&gt;

&lt;p&gt;Now, your Flask app will need to be visible from the web, so Twilio can send requests to it. ngrok lets you do this: with ngrok installed, run &lt;code&gt;ngrok http 5000&lt;/code&gt; in a new terminal tab in the directory your code is in.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FXcPIre7Cyh3ExMP9J-F7i9bnmi0OmurmQYB_Ggr9VG-.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FXcPIre7Cyh3ExMP9J-F7i9bnmi0OmurmQYB_Ggr9VG-.format-webp.webp" alt="ngrok terminal with forwarding ngrok url"&gt;&lt;/a&gt;&lt;br&gt;
You should see the screen above. Grab that ngrok &lt;strong&gt;Forwarding URL&lt;/strong&gt; to configure your Twilio number: select your Twilio number under &lt;strong&gt;Active Numbers&lt;/strong&gt; in your Twilio console, scroll to the Messaging section, and then modify the phone number’s routing by pasting the ngrok URL in the textbox corresponding to when &lt;strong&gt;A Message Comes In&lt;/strong&gt; as shown below:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FN08iaUMuglgBJXr3DtXCrJQfagKhVG5wjwH9j645-aK.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FN08iaUMuglgBJXr3DtXCrJQfagKhVG5wjwH9j645-aK.format-webp.webp" alt="configure Twilio phone number when a message comes in with webhook aka ngrok url"&gt;&lt;/a&gt;&lt;br&gt;
Click &lt;strong&gt;Save&lt;/strong&gt; and now your Twilio phone number is configured so that it maps to your web application server running locally on your computer. Let's build that application now.&lt;/p&gt;
&lt;h2&gt;
  
  
  Generate text through LangChain with OpenAI in Python via SMS
&lt;/h2&gt;

&lt;p&gt;Inside your &lt;code&gt;lc-sms&lt;/code&gt; directory, make a new file called &lt;em&gt;app.py&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Copy and paste the following code into &lt;em&gt;app.py&lt;/em&gt; to start off the ChatGPT-like SMS app to import the required libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twilio.twiml.messaging_response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessagingResponse&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.llms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, make the OpenAI LLM object (which could be another LLM--this is where LangChain can make reusability easier!), passing it the model name and API key from the .env file, and create a Flask application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-davinci-003&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;openai_api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the start of the &lt;em&gt;/sms&lt;/em&gt; webhook containing a TwiML response to respond to inbound SMS with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/sms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sms_reply&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Start our TwiML response
&lt;/span&gt;    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following template code below to help shape the queries and answers from the LLM. In this tutorial, it's Warriors basketball-themed. (Yes, I know the 2023 NBA Finals are the Heat versus the Nuggets. A girl can dream.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Answer the question based on the context below. If the question cannot be answered using the information provided, answer with &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t know, but the Warriors are the best team in the NBA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.
    Context: Steph Curry has won 4 NBA Finals series. His Golden State Warriors defeated the Cleveland Cavaliers three times and the Boston Celtics once. 

In 2015 Steph Curry and the Warriors defeated the Cleveland Cavaliers. The Cavs featured LeBron James, Kyrie Irving and not much else! 
In 2017 Steph Curry, Kevin Durant and the Warriors defeated the Cavs again. The Cavs still had Lebron and Kyrie.
In 2018 the Warriors, featuring Steph and KD again, defeated the Cavs for the third time in four years. The Cavs still had Kyrie and Lebron. 
In 2022 Steph and the Warriors defeated the Boston Celtics for his fourth title. The Celtics featured Jayson Tatum and Jaylen Brown. Steph Curry and the Golden State Warriors lost one NBA Finals series to the Cleveland Cavaliers and one to the Toronto Raptors. 

In 2016 Steph, Klay, Draymond and the rest of the Warriors lost to the Cleveland Cavaliers.  The Cavs starred LeBron James and Kyrie Irving. 
In 2019 the Steph and the Warriors, missing an injured KD, lost to the Toronto Raptors. The Raptors featured Kawhi Leonard in his only season in Canada alongside Kyle Lowry and Pascal Siakham. Steph Curry has only won one NBA Finals MVP up to this point in his career. . 

In 2022 Steph Curry averaged 31 points, 6 rebounds and 5 assists per game to win the MVP award in the Warriors 6-game defeat of the Boston Celtics.
In 2015 Andre Iguodala won the MVP in the Warriors defeat of the Cavs.
In both 2017 &amp;amp; 2018 Kevin Durant was Finals MVP in the Warriors victories over the Cavs. Steph Curry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s 4-2 NBA Finals record puts him ahead of many NBA greats including Larry Bird (3-2) and LeBron James (4-6). Steph still comes up short of the greatest NBA Finals winners including Bill Russell (11-1) and Michael Jordan (6-0).  
    Question: {query}
    Answer: &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;basketball_query_template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;input_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get the user's question via inbound text message, print out the answer by passing the question to the prompt template and formatting it, and then pass that to Twilio as the text message to send back to the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;basketball_query_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;basketball_query_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a new terminal tab (while the other terminal tab is still running &lt;code&gt;ngrok http 5000&lt;/code&gt;), run &lt;code&gt;python app.py&lt;/code&gt;. You can now text your Twilio number (questions about the Warriors because we provided the model Warriors context) configured from above.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FxeH7XRlqgg-3af6Hp0dhAr4ZbvDBlxHi9UUEF3zek9f.format-webp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FxeH7XRlqgg-3af6Hp0dhAr4ZbvDBlxHi9UUEF3zek9f.format-webp.webp" alt="sms example asking for a limerick about the warriors beating the celtics and who did the warriors beat in 2015 it was the Cavs"&gt;&lt;/a&gt;&lt;br&gt;
The &lt;a href="https://github.com/elizabethsiegle/langchain-twilio-sms" rel="noopener noreferrer"&gt;complete code can be found here on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next for Twilio and LangChain
&lt;/h2&gt;

&lt;p&gt;LangChain can be used for chatbots (not just Warriors/basketball-themed), chaining different tasks, Generative Question-Answering (GQA), summarization, and so much more. Stay tuned to the Twilio blog for more LangChain and Twilio content, and let me know online what you're building with AI!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter: &lt;a href="http://twitter.com/lizziepika" rel="noopener noreferrer"&gt;@lizziepika&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="http://github.com/elizabethsiegle" rel="noopener noreferrer"&gt;elizabethsiegle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:lsiegle@twilio.com"&gt;lsiegle@twilio.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>langchain</category>
      <category>twilio</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Fine-Tune an OpenAI ML Model with Node.js</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Tue, 02 May 2023 17:40:54 +0000</pubDate>
      <link>https://dev.to/twilio/how-to-fine-tune-an-openai-ml-model-with-nodejs-4c9j</link>
      <guid>https://dev.to/twilio/how-to-fine-tune-an-openai-ml-model-with-nodejs-4c9j</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/finetune-openai-ml-model-node" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;br&gt;
OpenAI, the creator of &lt;a href="https://chat.openai.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;, offers a &lt;a href="https://platform.openai.com/docs/models" rel="noopener noreferrer"&gt;variety of Machine Learning models developers can use with different capabilities and price points&lt;/a&gt;. Their API lets developers interface with the models, adding features like text completion, code completion, text or image generation, speech-to-text, and more to their applications.&lt;/p&gt;

&lt;p&gt;ChatGPT's model is powerful (&lt;a href="https://www.twilio.com/blog/sms-chatbot-openai-api-node" rel="noopener noreferrer"&gt;this Twilio tutorial went over how to Build a Serverless ChatGPT SMS Chatbot with the OpenAI API&lt;/a&gt;), but still limited. This tutorial will go over how to fine-tune an existing model from OpenAI with your own data so you can get more out of it.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is Fine-tuning?
&lt;/h3&gt;

&lt;p&gt;OpenAI's models have been trained on a large amount of text from the web, but their training data is still finite and not as current as they could be: they still miss information on specific topics.&lt;/p&gt;

&lt;p&gt;Transfer learning is a machine learning method where you use knowledge that was gained from solving one problem and apply it to a new but related problem. A model developed for a task is reused as the starting point for a model on a second task–for example, a model trained to specifically recognize pizza could be edited to recognize calzones.&lt;/p&gt;

&lt;p&gt;Fine-tuning is similar to transfer learning in that it lets you modify and customize an existing ML model.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Fine-tune?
&lt;/h3&gt;

&lt;p&gt;Fine-tuning helps you achieve better results on a wide number of tasks. Once a model is fine-tuned, you won't need to provide samples via prompt anymore, helping you save on costs and enabling lower-latency requests.&lt;/p&gt;

&lt;p&gt;Assuming the original task the model was trained for is similar to the new task, fine-tuning a model that has already been designed and trained allows developers to take advantage of what the model has already learned without having to develop it from scratch.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;OpenAI Account - &lt;a href="https://platform.openai.com/signup" rel="noopener noreferrer"&gt;create an OpenAI Account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Node.js installed - &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;download Node.js here&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After making an OpenAI account, you'll need an API Key. You can get an OpenAI API Key here by clicking on &lt;strong&gt;+ Create new secret key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Save that API key for later.&lt;/p&gt;
&lt;h3&gt;
  
  
  Make a New Node.js project
&lt;/h3&gt;

&lt;p&gt;To get started, create a new Node.js project in an empty directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;finetune-model
&lt;span class="nb"&gt;cd &lt;/span&gt;finetune-model
npm init &lt;span class="nt"&gt;--y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get started with OpenAI
&lt;/h3&gt;

&lt;p&gt;Make a &lt;em&gt;.env&lt;/em&gt; file in your root directory and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR-OPENAI-API-KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR-OPENAI-API-KEY&lt;/code&gt; with the OpenAI API Key you took note of earlier. Now you will be able to access this API Key in your code with &lt;code&gt;process.env.OPENAI_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, install the &lt;code&gt;openai&lt;/code&gt; and &lt;code&gt;fs&lt;/code&gt; npm packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;openai fs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's time to prepare your data!&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare Custom Data
&lt;/h3&gt;

&lt;p&gt;Training data is how GPT-3 learns what you want it to say.&lt;/p&gt;

&lt;p&gt;Your data should be in a &lt;a href="https://jsonlines.org/" rel="noopener noreferrer"&gt;JSONL document&lt;/a&gt;, where each line is a prompt-completion pair corresponding to a training sample.&lt;/p&gt;

&lt;p&gt;Make a jsonl-file called &lt;em&gt;newdata.jsonl&lt;/em&gt; and include prompt-completion pairs that look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;prompt text&amp;gt; -&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"completion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ideal generated text&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;prompt text&amp;gt; -&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"completion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ideal generated text&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;prompt text&amp;gt; -&amp;gt; "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"completion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ideal generated text&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data I used includes &lt;a href="https://github.com/elizabethsiegle/finetune-openai-model/blob/main/newdata.jsonl" rel="noopener noreferrer"&gt;fun facts about me and can be found here on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;OpenAI recommends having at least a couple hundred examples and found that &lt;a href="https://platform.openai.com/docs/guides/fine-tuning/general-best-practices" rel="noopener noreferrer"&gt;each doubling of dataset size leads to a linear increase in model quality&lt;/a&gt;. Save at least thirty prompt-completion pairs (the more the better). This is not enough to make a fine-tuned model for a production-ready product, but is enough for the purposes of this tutorial.&lt;/p&gt;

&lt;p&gt;Save your jsonl file, and on the command line, use &lt;a href="https://platform.openai.com/docs/guides/fine-tuning/cli-data-preparation-tool" rel="noopener noreferrer"&gt;OpenAI's CLI data preparation tool&lt;/a&gt; to help you easily convert your data into the jsonl file format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openai tools fine_tunes.prepare_data &lt;span class="nt"&gt;-f&lt;/span&gt; newdata.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With your prepared data, it's time to write some code to fine-tune an existing OpenAI machine learning model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine-tune an OpenAI Model
&lt;/h3&gt;

&lt;p&gt;In this tutorial, you'll use &lt;code&gt;davinci&lt;/code&gt;, but OpenAI currently lets you fine-tune its base models which also include &lt;code&gt;curie&lt;/code&gt;, &lt;code&gt;babbage&lt;/code&gt;, and &lt;code&gt;ada&lt;/code&gt;. These are their original models and they lack any instruction after training (which &lt;code&gt;text-davinci-003&lt;/code&gt; has, for example).&lt;/p&gt;

&lt;p&gt;In a file called finetune.js, add the following code to the top which will be used for each step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OpenAIApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAIApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beneath that, your first function uploads the jsonl file so you can use it next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newdata.jsonl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fine-tune&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`File ID &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;err uploadfile: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you run the file with &lt;code&gt;node finetune.js&lt;/code&gt;, take note of the ID printed out for the next step and comment out the &lt;code&gt;uploadFile()&lt;/code&gt; function call.&lt;/p&gt;

&lt;p&gt;Beneath the &lt;code&gt;uploadFile()&lt;/code&gt; function, add the following function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeFineTune&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFineTune&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;training_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-FILE-ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;davinci&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;err makefinetune: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;makeFineTune&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR-FILE-ID&lt;/code&gt; with the file ID from the previous step, and rerun &lt;em&gt;finetune.js&lt;/em&gt; to run the new function. After running it, comment out the &lt;code&gt;makeFineTune()&lt;/code&gt; function call.&lt;/p&gt;

&lt;p&gt;Wait a few minutes to let it process. You can check-in on the status of the fine tune, and additionally get the model ID, by calling the &lt;code&gt;listFineTunes&lt;/code&gt; API method as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFineTunedModelName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listFineTunes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fine_tuned_model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;err getmod: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;getFineTunedModelName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fine-tuned model is not ready for use when &lt;code&gt;status&lt;/code&gt; is &lt;code&gt;running&lt;/code&gt; and &lt;code&gt;fine_tuned_model&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, but once you see &lt;code&gt;status&lt;/code&gt; is &lt;code&gt;succeeded&lt;/code&gt; and &lt;code&gt;fine_tuned_model&lt;/code&gt; is something like &lt;code&gt;davinci:ft-personal-2023-05-01-22-05-28&lt;/code&gt;, then comment out &lt;code&gt;getFineTunedModelName()&lt;/code&gt;, copy the model name (beginning with &lt;code&gt;davinci&lt;/code&gt;), and you're good to go and use the model!&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your Fine-Tuned OpenAI model
&lt;/h3&gt;

&lt;p&gt;Add the following code to your &lt;em&gt;finetune.js&lt;/em&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;comp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-FINETUNED-MODEL-NAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;What is Lizzie Siegle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;favorite&lt;/span&gt; &lt;span class="nx"&gt;food&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, //replace this prompt according to your data
            max_tokens: 200
        });
        if (comp.data) {
            console.log(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="na"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, comp.data.choices)
        }
    } catch (err) {
        console.log(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, err)
    }
}
run();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR-FINETUNED-MODEL-NAME&lt;/code&gt; with the model name from the previous step.&lt;/p&gt;

&lt;p&gt;Then, rerun &lt;code&gt;node finetune.js&lt;/code&gt; and you should see your new custom model deployed in action making a new completion according to the prompt you passed in!&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3wux4i9pdzk8q33bkqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3wux4i9pdzk8q33bkqe.png" alt="Example usage of fine-tuned model that returns dumplings when i ask what my favorite food is" width="800" height="270"&gt;&lt;/a&gt;&lt;br&gt;
Alternatively, you can test out your fine-tuned model in the &lt;a href="https://platform.openai.com/playground" rel="noopener noreferrer"&gt;OpenAI Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzehgyswzpbxwwv0y4gw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdzehgyswzpbxwwv0y4gw.png" alt="list of fine-tuned models to use in OpenAI Playground" width="684" height="872"&gt;&lt;/a&gt;&lt;br&gt;
Complete code can be found here on GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for Fine-tuning OpenAI models?
&lt;/h3&gt;

&lt;p&gt;That's how to customize one of OpenAI's existing models to fit your own specific use case and data!&lt;/p&gt;

&lt;p&gt;For next steps, you can&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;add more data to improve completions and make the model production-ready&lt;/li&gt;
&lt;li&gt;try fine-tuning a different model other than &lt;code&gt;davinci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;use all the other &lt;a href="https://platform.openai.com/docs/api-reference/completions" rel="noopener noreferrer"&gt;Completions&lt;/a&gt; parameters like &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;frequency_penalty&lt;/code&gt;, &lt;code&gt;presence_penalty&lt;/code&gt;, etc., on your requests to fine-tuned models&lt;/li&gt;
&lt;li&gt;fine-tune a model in another programming language&lt;/li&gt;
&lt;li&gt;and more!&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>machinelearning</category>
      <category>openai</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to generate TwiML using Strings in C#</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Tue, 21 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/how-to-generate-twiml-using-strings-in-c-5afi</link>
      <guid>https://dev.to/twilio/how-to-generate-twiml-using-strings-in-c-5afi</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/generate-twiml-using-strings-in-csharp" rel="noopener noreferrer"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the decades, C# has added different ways to create a string, each with their own benefit. In this tutorial, you'll learn how to generate TwiML using the different C# string features with an &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0" rel="noopener noreferrer"&gt;ASP.NET Core Minimal API&lt;/a&gt; and compare it to the object oriented way of generating TwiML.&lt;/p&gt;

&lt;p&gt;But first, let's get you up to speed on how Twilio uses webhooks and TwiML to respond to text messages and voice calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Here's what you will need to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0" rel="noopener noreferrer"&gt;.NET 6 SDK&lt;/a&gt;, or &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0" rel="noopener noreferrer"&gt;.NET 7 SDK to use Raw String Literals&lt;/a&gt; (later versions will work too)&lt;/li&gt;
&lt;li&gt;A code editor or IDE. I recommend &lt;a href="https://www.jetbrains.com/rider/" rel="noopener noreferrer"&gt;JetBrains Rider&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/vs/preview/" rel="noopener noreferrer"&gt;Visual Studio 2022 Preview&lt;/a&gt; (will also work in the non-preview Visual Studio in the future), or &lt;a href="https://code.visualstudio.com/Download" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp" rel="noopener noreferrer"&gt;the C# plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A free Twilio account (&lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up with Twilio for free&lt;/a&gt; and get trial credit)&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;Twilio Phone Number&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok CLI&lt;/a&gt;, and optionally, a free &lt;a href="https://dashboard.ngrok.com/signup" rel="noopener noreferrer"&gt;ngrok account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Experience with ASP.NET Core and Minimal APIs is not required but recommended. Here's an article that brings you up to speed on &lt;a href="https://www.twilio.com/blog/sms-voice-dotnet-6-minimal-api" rel="noopener noreferrer"&gt;how to integrate ASP.NET Core Minimal APIs with Twilio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals" rel="noopener noreferrer"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it if you run into any issues, or &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals/issues" rel="noopener noreferrer"&gt;submit an issue&lt;/a&gt;, if you run into problems.&lt;/p&gt;

&lt;p&gt;Before creating your application, let's get you up to speed on how Twilio uses webhooks and TwiML to respond to text messages and voice calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks and TwiML
&lt;/h2&gt;

&lt;p&gt;Using Twilio, you can build programmatic text message and voice call applications. Twilio can do a lot of things like playing audio, gathering input, and recording a call. But Twilio doesn't know how &lt;strong&gt;you&lt;/strong&gt; want to respond to voice calls and text messages. That's why when Twilio receives a call or text message, Twilio will send an HTTP request to your application asking for instructions.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.twilio.com/docs/glossary/what-is-a-webhook" rel="noopener noreferrer"&gt;webhook&lt;/a&gt; is a user-defined HTTP callback. When an event happens in a service, your application is notified of that event using an HTTP request.&lt;/p&gt;

&lt;p&gt;Twilio uses webhooks heavily throughout all its products. Here's a diagram of what it looks like when there's an incoming text message to your Twilio Phone Number and your application is handling the messaging webhook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngu4vf8gdedwmu8gc9x3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngu4vf8gdedwmu8gc9x3.png" alt="Phone texts " width="500" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your Twilio Phone Number receives a text message, Twilio will send an HTTP request with the message details to your application. Your application then has to respond with &lt;a href="https://www.twilio.com/docs/glossary/what-is-twilio-markup-language-twiml" rel="noopener noreferrer"&gt;TwiML&lt;/a&gt; ( &lt;strong&gt;Twi&lt;/strong&gt; lio &lt;strong&gt;M&lt;/strong&gt; arkup &lt;strong&gt;L&lt;/strong&gt; anguage) to instruct Twilio how to respond. TwiML is &lt;a href="https://en.wikipedia.org/wiki/XML" rel="noopener noreferrer"&gt;XML&lt;/a&gt; with special tags defined by Twilio to provide instructions on how to respond to messages and voice calls. In the diagram depicted above, Twilio will respond with "Hi!" because the app responded with the following TwiML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Hi!&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more about &lt;a href="https://www.twilio.com/docs/messaging/twiml" rel="noopener noreferrer"&gt;TwiML for Programmable Messaging here&lt;/a&gt; and &lt;a href="https://www.twilio.com/docs/voice/twiml" rel="noopener noreferrer"&gt;TwiML for Programmable Voice here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an ASP.NET Core Minimal API to handle voice calls
&lt;/h2&gt;

&lt;p&gt;First, let's create an ASP.NET Core Minimal API to handle voice calls without using Raw String Literals.&lt;/p&gt;

&lt;p&gt;Open a terminal and create a new ASP.NET Core Minimal API project using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-o&lt;/span&gt; TwimlStrings 
&lt;span class="nb"&gt;cd &lt;/span&gt;TwimlStrings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will create the project in a folder named &lt;em&gt;TwimlStrings&lt;/em&gt; and navigate into the folder.&lt;/p&gt;

&lt;p&gt;Open the project in your editor, and update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following C# code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;?xml version=\"1.0\" encoding=\"utf-8\"?&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;Response&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Gather action=\"/answer\" method=\"GET\" input=\"speech\"&amp;gt; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Say&amp;gt;What does the fox say?&amp;lt;/Say&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;/Gather&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Say&amp;gt;Ring-ding-ding-ding-dingeringeding!&amp;lt;/Say&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;?xml version=\"1.0\" encoding=\"utf-8\"?&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;Response&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Say&amp;gt;You said: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;/Say&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your Twilio Phone Number receives a call, Twilio will send an HTTP GET request to the &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint, which will set the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" rel="noopener noreferrer"&gt;content-type&lt;/a&gt; response header to &lt;code&gt;application/xml&lt;/code&gt;, and return the following TwiML in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&amp;lt;/Gather&amp;gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's not very readable, so here's the formatted version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Gather&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Twilio receives the TwiML, it'll ask the caller "What does the fox say?" and listen for a speech response. When the caller responds, Twilio will send an HTTP request, this time to the &lt;em&gt;/answer&lt;/em&gt; endpoint, with the transcription of what the caller said. If the caller doesn't respond, Twilio will say "Ring-ding-ding-ding-dingeringeding!" to the caller.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;/answer&lt;/em&gt; endpoint will retrieve the transcription from the query string parameter &lt;code&gt;SpeechResult&lt;/code&gt; by binding it to the &lt;code&gt;speechResult&lt;/code&gt; parameter. The &lt;code&gt;speechResult&lt;/code&gt; is used to construct some more TwiML, but it is first escaped using &lt;code&gt;SecurityElement.Escape&lt;/code&gt; to ensure no XML is in the variable. The &lt;em&gt;/answer&lt;/em&gt; endpoint will generate the following TwiML when the caller says "ring ding ding":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;You said: ring ding ding&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Twilio receives this TwiML, Twilio will convert "You said: ring ding ding" to speech and stream the audio to the caller.&lt;/p&gt;

&lt;p&gt;In case you're not familiar with the song I am referencing in this app, &lt;a href="https://www.youtube.com/watch?v=jofNR_WkoCE" rel="noopener noreferrer"&gt;"What does the fox say?" originates from this famous song&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start your .NET application using your editor or using the terminal using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before integrating Twilio, open a browser and navigate to your web application URL with the suffix &lt;em&gt;/what-does-the-fox-say&lt;/em&gt;, to verify the TwiML that is generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the application publicly accessible
&lt;/h2&gt;

&lt;p&gt;For Twilio to be able to send HTTP requests to your local web server, the server needs to be publicly accessible. ngrok is a free and secure tunneling service that can make your local web server public.&lt;/p&gt;

&lt;p&gt;To start ngrok, run the following ngrok command in a separate terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http &lt;span class="o"&gt;[&lt;/span&gt;YOUR_ASPNET_URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[YOUR_ASPNET_URL]&lt;/code&gt; with the localhost URL from your .NET application. If you're using an HTTPS localhost URL, you'll need to &lt;a href="https://ngrok.com/docs/secure-tunnels#tunnel-authtokens" rel="noopener noreferrer"&gt;authenticate ngrok&lt;/a&gt;. The ngrok command will display an HTTPS &lt;strong&gt;Forwarding URL&lt;/strong&gt; that makes your local web server public.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feilej3w5xo41lmpcuy0v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feilej3w5xo41lmpcuy0v.png" alt="ngrok http command output showing information about the secure tunnel, most importantly the public forwarding URLs" width="500" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure your Twilio Phone Number
&lt;/h2&gt;

&lt;p&gt;Now it's time to update your Twilio Phone Number to send HTTP requests to your &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint via the ngrok Forwarding URL. The URL should look something like &lt;strong&gt;&lt;em&gt;&lt;a href="https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say" rel="noopener noreferrer"&gt;https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming?frameUrl=/console/phone-numbers/incoming?x-target-region=us1" rel="noopener noreferrer"&gt;the Active Phone Numbers section&lt;/a&gt; in the Twilio Console and click on your Twilio Phone Number.&lt;br&gt;&lt;br&gt;
This will take you to the configuration for the phone number. Find the &lt;strong&gt;Voice &amp;amp; Fax&lt;/strong&gt; section and under the " &lt;strong&gt;A CALL COMES IN&lt;/strong&gt;" label, set the dropdown to " &lt;strong&gt;Webhook&lt;/strong&gt;". In the text field next to it, enter your ngrok forwarding URL with &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; appended to it. Select “ &lt;strong&gt;HTTP GET&lt;/strong&gt; ” in the last dropdown. Finally, click the &lt;strong&gt;Save&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n30cb1cxb0fpvvwkn3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n30cb1cxb0fpvvwkn3o.png" alt="Voice &amp;amp; Fax section in the Twilio Phone Number configuration page. Under the " width="500" height="205"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Test that the application works
&lt;/h2&gt;

&lt;p&gt;To test out your application, call your Twilio Phone Number. You should hear the question "What does the fox say?" which you can respond to, wait 5 seconds, and then your response will be read back to you.&lt;/p&gt;

&lt;p&gt;After you've done that, stop your .NET application using your editor, or if you're using the terminal, press &lt;code&gt;ctrl + c&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Verbatim Strings
&lt;/h3&gt;

&lt;p&gt;The first way you can improve your code is by using &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim" rel="noopener noreferrer"&gt;Verbatim Strings&lt;/a&gt;. To turn your string into a Verbatim String, prefix your string with the "at" &lt;code&gt;@&lt;/code&gt; symbol.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;@"The fox said: ""I like using verbatim strings,
because it supports multi-line text,
you don't need to escape forward slashes (/), and backslashes (\),
and you can easily escape double quotes by doubling the double quotes""."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the Verbatim String above says, you don't need to escape forward slashes or backslashes, however you still need to escape double quotes. However, instead of using a backslash to escape the double quotes, you place two double quotes and the resulting character will be a single double quote.&lt;/p&gt;

&lt;p&gt;Applying this knowledge to your TwiML &lt;em&gt;Program.cs&lt;/em&gt; file, you will have the following endpoint code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;
 &amp;lt;Response&amp;gt;
 &amp;lt;Gather action=""/answer"" method=""GET"" input=""speech""&amp;gt; 
 &amp;lt;Say&amp;gt;What does the fox say?&amp;lt;/Say&amp;gt;
 &amp;lt;/Gather&amp;gt;
 &amp;lt;Say&amp;gt;Ring-ding-ding-ding-dingeringeding!&amp;lt;/Say&amp;gt;
 &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;
 &amp;lt;Response&amp;gt;
 &amp;lt;Say&amp;gt;You said: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;@"&amp;lt;/Say&amp;gt;
 &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting TwiML will be formatted better, but still be indented oddly because the tabs used to nicely indent the code are also included in the string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Gather&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using Verbatim Strings, you are able to use multi-line text so you don't have to concatenate the string for every line of TwiML. You may think that this will also improve performance as you aren't creating as many strings and then concatenating them. But the compiler will optimize your code so that there's no difference in the end result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use String Interpolation
&lt;/h3&gt;

&lt;p&gt;By using Verbatim Strings, you were able to decrease the amount of opening and closing double quotes to construct your TwiML string which makes the code a lot easier to read. However, you still had to use string concatenation to embed the &lt;code&gt;speechResult&lt;/code&gt; into your TwiML string for the &lt;em&gt;/answer&lt;/em&gt; endpoint.&lt;/p&gt;

&lt;p&gt;To embed variables in your string, you can use &lt;code&gt;String.Format&lt;/code&gt; or, much better, String Interpolation. First, start your string with the dollar &lt;code&gt;$&lt;/code&gt; sign then use the curly brackets &lt;code&gt;{}&lt;/code&gt; to embed C# expressions.&lt;/p&gt;

&lt;p&gt;For example, the following code will write "What does the fox say?" to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"fox"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"What does the &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; say?"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Applying this knowledge to the &lt;em&gt;/answer&lt;/em&gt; endpoint, you can further improve the code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;$@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Response&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Say&amp;gt;You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  C# 11 and .NET 7 only: Use Raw String Literals
&lt;/h3&gt;

&lt;p&gt;C# 11 is still in preview, but you can try it out today using the .NET 7 previews. Make sure you have .NET 7 installed and are using .NET 7 in your project. Then, open your project file at &lt;em&gt;TwimlStrings.csproj&lt;/em&gt; and add the &lt;code&gt;&amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;&lt;/code&gt; to the first &lt;code&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt; node. This will enable the C# 11 preview features.&lt;/p&gt;

&lt;p&gt;Your &lt;em&gt;TwimlStrings.csproj&lt;/em&gt; in the project folder, should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Web"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net7.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;preview&lt;span class="nt"&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have enabled C# 11, let's talk about the new &lt;a href="https://devblogs.microsoft.com/dotnet/csharp-11-preview-updates/" rel="noopener noreferrer"&gt;Raw String Literals&lt;/a&gt;. To use Raw String Literals, you start and end your string with at least 3 double quotes. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;because&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;escape&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="nf"&gt;slashes&lt;/span&gt; &lt;span class="p"&gt;(/),&lt;/span&gt; &lt;span class="nf"&gt;backslashes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"),
&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;better&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;having&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;indentation&lt;/span&gt; &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
&lt;span class="s"&gt;"""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax highlighting may be off in the next snippets and snippet above because the blog doesn't have C# 11 highlighting yet.&lt;/p&gt;

&lt;p&gt;As with Verbatim Strings, you don't have to escape forward slashes and backslashes, but, as opposed to Verbatim Strings, in Raw String Literals you also don't have to escape your double quotes, more or less.&lt;/p&gt;

&lt;p&gt;You can use double quotes as long as the number of subsequent double quotes is less than the amount of double quotes you started and ended your string with. This is clearer with an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 3 start and end double quotes, so 1 and 2 subsequent double quotes is allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This "&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;""" 
&lt;/span&gt;
&lt;span class="c1"&gt;// 3 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This """&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt;
&lt;span class="c1"&gt;// 4 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;""""&lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="s"&gt;""" is allowed.""""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lines one and two are valid Raw String Literals because the number of subsequent double quotes is lower than the number of double quotes used to start and end the Raw String Literal.&lt;/p&gt;

&lt;p&gt;Raw String Literals also makes it easier to format your strings. Take the following method that generates a Verbatim String as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateHtml&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;@"
 &amp;lt;p&amp;gt;
 This is a &amp;lt;b&amp;gt;multi-line Verbatim String&amp;lt;/b&amp;gt; to generate some HTML
 &amp;lt;/p&amp;gt;
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
 This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Verbatim String&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; to generate some HTML
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the newlines and the tab indentation? I formatted the Verbatim String for the sake of readability, but as a result I also got the newlines and tabs in it which I do not want.&lt;/p&gt;

&lt;p&gt;Let's take a look at the Raw String Literal version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""";
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Raw String Literal&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unnecessary newlines and tabs are gone. The way Raw String Literals indent your strings depends on where you place the starting and ending double quotes, and also how you indent the content of your string. It's a little funky and hard to explain all the variations, so I recommend experimenting with it and reading &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#raw-string-literals" rel="noopener noreferrer"&gt;the Microsoft documentation on Raw String Literals&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Using this knowledge, you can apply Raw String Literals to your endpoints like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;What&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;fox&lt;/span&gt; &lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Ring&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dingeringeding&lt;/span&gt;&lt;span class="p"&gt;!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""",
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;said&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""" ,
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the resulting TwiML will be readable and not contain unnecessary newlines and tabs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other ways to generate TwiML
&lt;/h3&gt;

&lt;p&gt;There are many other ways you could generate TwiML. Since TwiML is XML, you can use the APIs from the &lt;code&gt;System.Xml&lt;/code&gt; and &lt;code&gt;System.Xml.Linq&lt;/code&gt; namespace. Twilio also has a helper library for .NET that lets you generate TwiML in an object-oriented way, and the helper library for ASP.NET helps you return the TwiML in the HTTP response.&lt;/p&gt;

&lt;p&gt;In the terminal where you ran your project, run the following commands to add the &lt;a href="https://www.nuget.org/packages/Twilio" rel="noopener noreferrer"&gt;Twilio package&lt;/a&gt; and &lt;a href="https://www.nuget.org/packages/Twilio" rel="noopener noreferrer"&gt;Twilio.AspNet.Core package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Twilio
dotnet add package Twilio.AspNet.Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.AspNet.Core.MinimalApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML.Voice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gather&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Speech&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UriKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Relative&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What does the fox say?"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ring-ding-ding-ding-dingeringeding!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting TwiML will be the same as before. Notice, however, that you don't need to use the &lt;code&gt;SecurityElement.Escape&lt;/code&gt; anymore to escape the user input, because the Twilio helper library will XML encode it for you. Under the hood, the helper library uses the APIs from &lt;code&gt;System.Linq.Xml&lt;/code&gt; to generate the XML string which will also do the formatting for you.&lt;/p&gt;

&lt;p&gt;Now, you may be wondering, why would you use strings when you can use the Twilio helper library, or maybe you're wondering why you would use the helper library when you can use strings.&lt;/p&gt;

&lt;p&gt;The advantages of creating TwiML using strings, is that you don't need any dependencies and it's more memory efficient &amp;amp; performant. However, it is easier to make a mistake in your TwiML and there will be no compilation errors to prevent you from doing that. You also have to XML escape user input yourself to protect yourself from XML injection.&lt;/p&gt;

&lt;p&gt;The advantages of using the Twilio helper library are that you are using fully typed objects and methods that provide &lt;a href="https://en.wikipedia.org/wiki/Intelligent_code_completion" rel="noopener noreferrer"&gt;IntelliSense&lt;/a&gt; in IDEs. If you make a typo, your project will not build and you will receive a compiler error telling you where your typo is. However, you do depend on the Twilio helper library and its dependencies, and you have to create a bunch of objects that are ultimately serialized to an XML string which is less memory efficient and slower.&lt;/p&gt;

&lt;p&gt;They both have their own advantages and disadvantages, but you can mix and match based on your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You learned how Twilio uses webhooks and TwiML to give you control over how to respond to a call or text message. You then learned how to generate TwiML using different string features in C#, such as Verbatim Strings, String Interpolation, and C# 11's new Raw String Literal feature. You then looked at the difference between generating TwiML using strings and generating TwiML using the Twilio helper library.&lt;/p&gt;

&lt;p&gt;Here are a couple more resources to further your learning on Minimal APIs and Twilio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/forward-voicemails-with-transcript-to-your-email-using-csharp-and-aspnetcore" rel="noopener noreferrer"&gt;Forward Voicemails with Transcript to your Email using C# and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/find-us-representatives-congressional-districts-with-sms-aspnetcore" rel="noopener noreferrer"&gt;Find your U.S. Representatives and Congressional Districts with SMS and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/test-aspnetcore-minimal-apis" rel="noopener noreferrer"&gt;How to test ASP.NET Core Minimal APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can't wait to see what you build. Let us know!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Use Raw String Literals to generate TwiML in C# 11</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Wed, 15 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/use-raw-string-literals-to-generate-twiml-in-c-11-3pmp</link>
      <guid>https://dev.to/twilio/use-raw-string-literals-to-generate-twiml-in-c-11-3pmp</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/use-raw-string-literals-to-generate-twiml-in-csharp11"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the decades, C# added different ways to create a string, each with their own benefit, and C# 11 is adding another way:  &lt;strong&gt;Raw String Literals&lt;/strong&gt;. There are many use cases for Raw String Literals, and one of those use cases is to generate TwiML, but more on that later. In this tutorial, you'll learn how to generate TwiML with Raw String Literals with an ASP.NET Core Minimal API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Here's what you will need to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0"&gt;.NET 7 SDK&lt;/a&gt; (later versions will work too)&lt;/li&gt;
&lt;li&gt;A code editor or IDE (I recommend &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/vs/preview/"&gt;Visual Studio 2022 Preview&lt;/a&gt; (C# 11 is only supported in preview now, but will work in non-preview in the future.), or &lt;a href="https://code.visualstudio.com/Download"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp"&gt;the C# plugin&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A free Twilio account (&lt;a href="https://www.twilio.com/try-twilio"&gt;sign up with Twilio for free&lt;/a&gt; and get trial credit)&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console"&gt;Twilio Phone Number&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://ngrok.com/download"&gt;ngrok CLI&lt;/a&gt;, and optionally, a free &lt;a href="https://dashboard.ngrok.com/signup"&gt;ngrok account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Experience with ASP.NET Core and Minimal APIs is not required but recommended. Here's an article that brings you up to speed on &lt;a href="https://www.twilio.com/blog/sms-voice-dotnet-6-minimal-api"&gt;how to integrate ASP.NET Core Minimal APIs with Twilio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it if you run into any issues, or &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals/issues"&gt;submit an issue&lt;/a&gt;, if you run into problems.&lt;/p&gt;

&lt;p&gt;Before creating your application, let's get you up to speed on how Twilio uses webhooks and TwiML to respond to text messages and voice calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks and TwiML
&lt;/h2&gt;

&lt;p&gt;Using Twilio, you can build programmatic text message and voice call applications. Twilio can do a lot of things like playing audio, gathering input, and recording a call, but Twilio doesn't know what &lt;strong&gt;you&lt;/strong&gt; want to do in response to voice calls and text messages. That's why when Twilio receives a call or text message, Twilio will send an HTTP request to your application asking for instructions.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.twilio.com/docs/glossary/what-is-a-webhook"&gt;webhook&lt;/a&gt; is a user-defined HTTP callback. When an event happens in a service, your application is notified of that event using an HTTP request.&lt;/p&gt;

&lt;p&gt;Twilio uses webhooks heavily throughout all its products. Here's a diagram of what it looks like when there's an incoming text message to your Twilio Phone Number and your application is handling the messaging webhook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dXfDdO_a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/vQXGk8VFTcIBytBwOohc9SbQH1vKWzeKNmzR3Q4Qgu26TR.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dXfDdO_a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/vQXGk8VFTcIBytBwOohc9SbQH1vKWzeKNmzR3Q4Qgu26TR.width-500.png" alt='Phone texts "Ahoy!" to a Twilio Phone Number, Twilio sends the SMS details (from and to phone number and the body of the message) via HTTP to your web application, then your application responds with TwiML instructions instructing to respond with "Hi!". Twilio receives the instructions and sends "Hi!" back to the original sender.' width="500" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your Twilio Phone Number receives a text message, Twilio will send an HTTP request with the message details to your application. Your application then has to respond with &lt;a href="https://www.twilio.com/docs/glossary/what-is-twilio-markup-language-twiml"&gt;TwiML&lt;/a&gt; ( &lt;strong&gt;Twi&lt;/strong&gt; lio &lt;strong&gt;M&lt;/strong&gt; arkup &lt;strong&gt;L&lt;/strong&gt; anguage) to instruct Twilio how to respond. TwiML is &lt;a href="https://en.wikipedia.org/wiki/XML"&gt;XML&lt;/a&gt; with special tags defined by Twilio to provide instructions on how to respond to messages and voice calls. In the diagram depicted above, Twilio will respond with "Hi!" because the app responded with the following TwiML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Hi!&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more about &lt;a href="https://www.twilio.com/docs/messaging/twiml"&gt;TwiML for Programmable Messaging here&lt;/a&gt; and &lt;a href="https://www.twilio.com/docs/voice/twiml"&gt;TwiML for Programmable Voice here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that you know what webhooks and TwiML are, let's create a voice call application with ASP.NET Core.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an ASP.NET Core Minimal API to handle voice calls
&lt;/h2&gt;

&lt;p&gt;First, let's create an ASP.NET Core Minimal API to handle voice calls without using Raw String Literals.&lt;/p&gt;

&lt;p&gt;Open a terminal and create a new ASP.NET Core Minimal API project using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-o&lt;/span&gt; TwimlRawStringLiterals 
&lt;span class="nb"&gt;cd &lt;/span&gt;TwimlRawStringLiterals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will create the project in a folder named &lt;em&gt;TwimlRawStringLiterals&lt;/em&gt; and navigate into the folder.&lt;/p&gt;

&lt;p&gt;Open the project in your editor, and update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following C# code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;
 &amp;lt;Response&amp;gt;
 &amp;lt;Gather action=""/answer"" method=""GET"" input=""speech""&amp;gt; 
 &amp;lt;Say&amp;gt;What does the fox say?&amp;lt;/Say&amp;gt;
 &amp;lt;/Gather&amp;gt;
 &amp;lt;Say&amp;gt;Ring-ding-ding-ding-dingeringeding!&amp;lt;/Say&amp;gt;
 &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;$@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Response&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Say&amp;gt;You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your Twilio Phone Number receives a call, Twilio will send an HTTP GET request to the &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint, which will set the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type"&gt;content-type&lt;/a&gt; response header to &lt;code&gt;application/xml&lt;/code&gt;, and return the following TwiML in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Gather&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take note of how the &lt;code&gt;&amp;lt;Response&amp;gt;&lt;/code&gt; node and everything in it is indented with a tab too much.&lt;/p&gt;

&lt;p&gt;When Twilio receives the TwiML, it'll ask the caller "What does the fox say?" and listen for a speech response. When the caller responds, Twilio will send an HTTP request, this time to the &lt;em&gt;/answer&lt;/em&gt; endpoint, with the transcription of what the caller said. If the caller doesn't respond, Twilio will say "Ring-ding-ding-ding-dingeringeding!" to the caller.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;/answer&lt;/em&gt; endpoint will retrieve the transcription from the query string parameter &lt;code&gt;SpeechResult&lt;/code&gt; by binding it to the &lt;code&gt;speechResult&lt;/code&gt; parameter. The &lt;code&gt;speechResult&lt;/code&gt; is used to construct some more TwiML, but it is first escaped using &lt;code&gt;SecurityElement.Escape&lt;/code&gt; to ensure no XML is in the variable. The &lt;em&gt;/answer&lt;/em&gt; endpoint will generate the following TwiML when the caller says "ring ding ding":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;You said: ring ding ding&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Twilio receives this TwiML, Twilio will convert "You said: ring ding ding" to speech and stream the audio to the caller.&lt;/p&gt;

&lt;p&gt;In case you're not familiar with the song I am referencing in this app, &lt;a href="https://www.youtube.com/watch?v=jofNR_WkoCE"&gt;"What does the fox say?" originates from this famous song&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start your .NET application using your editor or using the terminal using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before integrating Twilio, open a browser and navigate to your web application URL with the suffix &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; to verify the TwiML that is generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the application publicly accessible
&lt;/h2&gt;

&lt;p&gt;For Twilio to be able to send HTTP requests to your local web server, the server needs to become publicly accessible. ngrok is a free secure tunneling service that can make your local web servers public.&lt;/p&gt;

&lt;p&gt;To start ngrok, run the following ngrok command in a separate terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http &lt;span class="o"&gt;[&lt;/span&gt;YOUR_ASPNET_URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[YOUR_ASPNET_URL]&lt;/code&gt; with the localhost URL from your .NET application. If you're using an HTTPS localhost URL, you'll need to &lt;a href="https://ngrok.com/docs/secure-tunnels#tunnel-authtokens"&gt;authenticate ngrok&lt;/a&gt;. The ngrok command will display an HTTPS &lt;strong&gt;Forwarding URL&lt;/strong&gt; that makes your local web server public.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hWStHNyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/apEiL_JFTXcwkjo0vLBlJd7XZjiujP63mGVlhdM-EZ7fNq.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hWStHNyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/apEiL_JFTXcwkjo0vLBlJd7XZjiujP63mGVlhdM-EZ7fNq.width-500.png" alt="ngrok http command output showing information about the secure tunnel, most importantly the public forwarding URLs" width="500" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure your Twilio Phone Number
&lt;/h2&gt;

&lt;p&gt;Now it's time to update your Twilio Phone Number to send HTTP requests to your &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint via the ngrok Forwarding URL. The URL should look something like _ &lt;strong&gt;&lt;a href="https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say"&gt;https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say&lt;/a&gt;&lt;/strong&gt; _.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming?frameUrl=/console/phone-numbers/incoming?x-target-region=us1"&gt;Active Phone Numbers section&lt;/a&gt; in the Twilio Console and click on your Twilio Phone Number.&lt;br&gt;&lt;br&gt;
This will take you to the configuration for the phone number. Find the &lt;strong&gt;Voice &amp;amp; Fax&lt;/strong&gt; section and under the " &lt;strong&gt;A CALL COMES IN&lt;/strong&gt;" label, set the dropdown to " &lt;strong&gt;Webhook&lt;/strong&gt;". In the text field next to it, enter your ngrok forwarding URL with &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; appended to it. Select “ &lt;strong&gt;HTTP GET&lt;/strong&gt; ” in the last dropdown. Finally, click the &lt;strong&gt;Save&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gQ1AuUPj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/rcWjPSxeiAd8YuogrtDL7nBYmVsw1VBYSKHKI-wwCilS4x.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gQ1AuUPj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/rcWjPSxeiAd8YuogrtDL7nBYmVsw1VBYSKHKI-wwCilS4x.width-500.png" alt='Voice &amp;amp; Fax section in the Twilio Phone Number configuration page. Under the "A CALL COMES IN" label, a dropdown is set to "Webhook", the text field next to it is configured with "https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say", and the dropdown next to that is set to "HTTP GET".' width="500" height="205"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Test that the application works
&lt;/h2&gt;

&lt;p&gt;To test out your application, call your Twilio Phone Number. You should hear the question "What does the fox say?" which you can respond to, wait 5 seconds, and then your response will be read back to you.&lt;/p&gt;

&lt;p&gt;After you've done that, stop your application using your editor, or if you're using the terminal, press &lt;code&gt;ctrl + c&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  C# 11 and .NET 7 only: Use Raw String Literals
&lt;/h3&gt;

&lt;p&gt;C# 11 is still in preview, but you can try it out today using the .NET 7 previews. Make sure you have .NET 7 installed and are using .NET 7 in your project. Then, open your project file at &lt;em&gt;TwimlRawStringLiterals.csproj&lt;/em&gt; and add the &lt;code&gt;&amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;&lt;/code&gt; to the first &lt;code&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt; node. This will enable the C# 11 preview features.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;TwimlRawStringLiterals.csproj&lt;/em&gt; file should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Web"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net7.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;preview&lt;span class="nt"&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have enabled C# 11, let's talk about the new &lt;a href="https://devblogs.microsoft.com/dotnet/csharp-11-preview-updates/"&gt;Raw String Literals&lt;/a&gt;. To use Raw String Literals, you start and end your string with at least 3 double quotes. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;because&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;escape&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="nf"&gt;slashes&lt;/span&gt; &lt;span class="p"&gt;(/),&lt;/span&gt; &lt;span class="nf"&gt;backslashes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"),
&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;better&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;having&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;indentation&lt;/span&gt; &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
&lt;span class="s"&gt;"""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax highlighting may be off in the next snippets and snippet above because the blog doesn't have C# 11 highlighting yet.&lt;/p&gt;

&lt;p&gt;You don't have to escape forward slashes, backslashes, and you also don't have to escape your double quotes, more or less. You can use double quotes as long as the number of subsequent double quotes is less than the amount of double quotes you started and ended your string with. This is clearer with an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 3 start and end double quotes, so 1 and 2 subsequent double quotes is allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This "&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;""" 
&lt;/span&gt;
&lt;span class="c1"&gt;// 3 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This """&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt;
&lt;span class="c1"&gt;// 4 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;""""&lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="s"&gt;""" is allowed.""""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line one and two are valid Raw String Literals because the number of subsequent double quotes is lower than the number of double quotes used to start and end the Raw String Literal.&lt;/p&gt;

&lt;p&gt;Raw String Literals also makes it easier to format your strings. Take the following method that generates a Verbatim String (&lt;code&gt;@&lt;/code&gt;) as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateHtml&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;@"
 &amp;lt;p&amp;gt;
 This is a &amp;lt;b&amp;gt;multi-line Verbatim String&amp;lt;/b&amp;gt; to generate some HTML
 &amp;lt;/p&amp;gt;
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
 This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Verbatim String&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; to generate some HTML
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the newlines and the tab indentation? I formatted the Verbatim String for the sake of readability, but as a result I also got the newlines and tabs in it which I do not want.&lt;/p&gt;

&lt;p&gt;Let's take a look at the Raw String Literal version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""";
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Raw String Literal&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unnecessary newlines and tabs are gone. The way Raw String Literals indent your strings depends on where you place the starting and ending double quotes, and also how you indent the content of your string. It's a little funky and hard to explain all the variations, so I recommend experimenting with it and reading &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#raw-string-literals"&gt;the Microsoft documentation on Raw String Literals&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Using this knowledge, you can apply Raw String Literals to your endpoints like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;What&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;fox&lt;/span&gt; &lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Ring&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dingeringeding&lt;/span&gt;&lt;span class="p"&gt;!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""",
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;said&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""" ,
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the resulting TwiML will be readable and not contain unnecessary newlines and tabs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other ways to generate TwiML
&lt;/h3&gt;

&lt;p&gt;There are many other ways you could generate TwiML. Since TwiML is XML, you can use the APIs from the &lt;code&gt;System.Xml&lt;/code&gt; and &lt;code&gt;System.Xml.Linq&lt;/code&gt; namespace. Twilio also has a helper library for .NET that lets you generate TwiML in an object-oriented way, and the helper library for ASP.NET helps you return the TwiML in the HTTP response.&lt;/p&gt;

&lt;p&gt;In the terminal where you ran your project, run the following commands to add the &lt;a href="https://www.nuget.org/packages/Twilio"&gt;Twilio package&lt;/a&gt; and &lt;a href="https://www.nuget.org/packages/Twilio"&gt;Twilio.AspNet.Core package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Twilio
dotnet add package Twilio.AspNet.Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.AspNet.Core.MinimalApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML.Voice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gather&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Speech&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UriKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Relative&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What does the fox say?"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ring-ding-ding-ding-dingeringeding!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting TwiML will be the same as before. Notice, however, that you don't need to use the &lt;code&gt;SecurityElement.Escape&lt;/code&gt; anymore to escape the user input, because the Twilio helper library will XML encode it for you.&lt;br&gt;&lt;br&gt;
Under the hood, the helper library uses the APIs from &lt;code&gt;System.Linq.Xml&lt;/code&gt; to generate the XML string which will also do the formatting for you.&lt;/p&gt;

&lt;p&gt;Now, you may be wondering, why would you use strings when you can use the Twilio helper library, or maybe you're wondering why you would use the helper library when you can use strings.&lt;/p&gt;

&lt;p&gt;The advantages of creating TwiML using strings, is that you don't need any dependencies and it's more memory efficient &amp;amp; performant. However, it is easier to make a mistake in your TwiML and there will be no compilation errors to prevent you from doing that. You also have to XML escape user input yourself to protect yourself from XML injection.&lt;/p&gt;

&lt;p&gt;The advantages of using the Twilio helper library are that you are using fully typed objects and methods that provide &lt;a href="https://en.wikipedia.org/wiki/Intelligent_code_completion"&gt;IntelliSense&lt;/a&gt; in IDEs. If you make a typo, your project will not build and you will receive a compiler error telling you where your typo is. However, you do depend on the Twilio helper library and its dependencies, and you have to create a bunch of objects that are ultimately serialized to an XML string which is less memory efficient and slower.&lt;/p&gt;

&lt;p&gt;They both have their own advantages and disadvantages, but you can mix and match based on your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You learned how Twilio uses webhooks and TwiML to give you control over how to respond to a call or text message. You then learned how to generate TwiML instructions using C# 11's new Raw String Literal feature, and compared it to generating TwiML with the Twilio helper library.&lt;/p&gt;

&lt;p&gt;Here are a couple more resources to further your learning on Minimal APIs and Twilio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/forward-voicemails-with-transcript-to-your-email-using-csharp-and-aspnetcore"&gt;Forward Voicemails with Transcript to your Email using C# and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/find-us-representatives-congressional-districts-with-sms-aspnetcore"&gt;Find your U.S. Representatives and Congressional Districts with SMS and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/test-aspnetcore-minimal-apis"&gt;How to test ASP.NET Core Minimal APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can't wait to see what you build. Let us know!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Use Visual Studio dev tunnels to handle Twilio Webhooks</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Wed, 08 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/use-visual-studio-dev-tunnels-to-handle-twilio-webhooks-58dc</link>
      <guid>https://dev.to/twilio/use-visual-studio-dev-tunnels-to-handle-twilio-webhooks-58dc</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/use-visual-studio-port-tunneling-with-twilio-webhooks" rel="noopener noreferrer"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio recently introduced a new feature called  &lt;strong&gt;dev tunnels&lt;/strong&gt;. By using dev tunnels, Visual Studio will create a new public URL (tunnel URL) for you, and HTTP requests sent to the tunnel URL will be forwarded to your ASP.NET Core project running on localhost.&lt;/p&gt;

&lt;p&gt;Dev tunnels has a lot of use cases. You could use this to easily test your web application on other devices like mobiles phone and tablets. You could also use this to make your application temporarily, publicly available for doing interactive demos and inviting your audience to participate.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use dev tunnels with Twilio
&lt;/h2&gt;

&lt;p&gt;The most exciting use case for Twilio is that you can use dev tunnels to test webhooks. &lt;a href="https://www.twilio.com/docs/glossary/what-is-a-webhook" rel="noopener noreferrer"&gt;What are webhooks again&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;" &lt;strong&gt;Webhooks are user-defined HTTP callbacks.&lt;/strong&gt; They are triggered by some event in a web application and can facilitate integrating different applications or third-party APIs, like Twilio."&lt;/p&gt;

&lt;p&gt;At Twilio, we heavily use webhooks throughout all of our products. You can use webhooks to respond to text messages or to manage voice calls. Here's a diagram of what it looks like when there's an incoming text message to your Twilio Phone Number and your application is handling the messaging webhook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl83dtzwfrugx6cqin6vv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl83dtzwfrugx6cqin6vv.png" alt="Phone texts " width="500" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your Twilio Phone Number receives a text message, Twilio will send an HTTP request with the message details to your application. Your application then has to respond with &lt;a href="https://www.twilio.com/docs/glossary/what-is-twilio-markup-language-twiml" rel="noopener noreferrer"&gt;TwiML&lt;/a&gt; (Twilio Markup Language) to instruct Twilio how to respond. TwiML is &lt;a href="https://en.wikipedia.org/wiki/XML" rel="noopener noreferrer"&gt;XML&lt;/a&gt; with special tags defined by Twilio to provide instructions on how to respond to messages and voice calls. In the diagram depicted above, Twilio will respond with "Hi!" because the app responded with TwiML that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Hi!&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more about &lt;a href="https://www.twilio.com/docs/messaging/twiml" rel="noopener noreferrer"&gt;TwiML for Programmable Messaging here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some other types of webhooks will inform you of an event and not expect instructions as a response. However, &lt;strong&gt;webhook requests can only be made to publicly available URLs&lt;/strong&gt; , and when you're developing and testing applications, you are usually doing so locally. But with Visual Studio dev tunnels, you can quickly make your web application public, so you can develop your webhooks!&lt;/p&gt;

&lt;p&gt;So when a text message comes in, instead of Twilio sending an HTTP request directly to your application, Twilio can send it to your Visual Studio tunnel which will forward it to your application running on localhost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftwilio-cms-prod.s3.amazonaws.com%2Fdocuments%2FDevTunnels.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftwilio-cms-prod.s3.amazonaws.com%2Fdocuments%2FDevTunnels.svg" alt="Diagram of the SMS webhook flow to an ASP.NET Core project tunneled via Visual Studio dev tunnels. Phone sending an SMS has arrows pointing back and forth to the Twilio logo, with arrows back and forth pointing to tunnels.api.visualstudio.com, with arrows pointing back and forth to a laptop with the Visual Studio logo on it." width="638" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn how to use Visual Studio to create an ASP.NET Core project that will handle the Twilio SMS webhook, use dev tunnels to make the locally running application public, and then configure a Twilio Phone Number to send webhook requests to the public tunnel URL whenever a text message comes in so you can respond to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You'll need the following things in this tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Windows machine (there is no information about support for Visual Studio for Mac yet.)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://visualstudio.microsoft.com/vs/preview/" rel="noopener noreferrer"&gt;Visual Studio 2022 Preview&lt;/a&gt;, with the ASP.NET and web development workload installed&lt;/li&gt;
&lt;li&gt;In Visual Studio, you need to enable the dev tunnels preview feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find details on how to do these steps in &lt;a href="https://devblogs.microsoft.com/visualstudio/dev-tunnels-in-visual-studio-for-asp-net-core-projects/" rel="noopener noreferrer"&gt;the dev tunnels announcement post by Microsoft&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly, you'll also need a Twilio account (trial or upgraded). &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;Try Twilio for free with promotional trial credit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/VsTunnelsSmsBot" rel="noopener noreferrer"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it as a reference if you run into any issues, or &lt;a href="https://github.com/Swimburger/VsTunnelsSmsBot/issues" rel="noopener noreferrer"&gt;submit an issue&lt;/a&gt; if you need assistance&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an empty ASP.NET Core web application with dev tunnels
&lt;/h2&gt;

&lt;p&gt;Open Visual Studio 2022 preview and click " &lt;strong&gt;Create a new project&lt;/strong&gt;", then search for the " &lt;strong&gt;ASP.NET Core Empty&lt;/strong&gt;" project template and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fritkipf7ncoa0svp9imo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fritkipf7ncoa0svp9imo.png" alt="Visual Studio " width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, give your project a meaningful name and click &lt;strong&gt;Next&lt;/strong&gt;. Then on the last screen, leave everything as is and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Visual Studio will now generate an empty ASP.NET Core application that returns "Hello World!" using Minimal APIs. Go ahead and run your project to see it in action by pressing the green play arrow with your project name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbhg5c7187xdphubk788.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbhg5c7187xdphubk788.png" alt="Visual Studio top toolbar with green play arrow next to the project name " width="500" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio will run your application, open your browser, and then browse to the web application URL, which will look similar to &lt;em&gt;&lt;a href="https://localhost:5000" rel="noopener noreferrer"&gt;https://localhost:5000&lt;/a&gt;&lt;/em&gt;. However, port number 5000 will be a random port number instead. The "Hello World!" application works as expected, but is only reachable on your machine. No other devices can reach it.&lt;/p&gt;

&lt;p&gt;Back in Visual Studio, click on the debug dropdown menu, and then select the &lt;strong&gt;Dev Tunnels&lt;/strong&gt; menu item. Now, the &lt;strong&gt;Dev Tunnels&lt;/strong&gt; fly out menu shows you different options, to either select a dev tunnel (but there are none yet), to create a new tunnel, or to open the dev tunnels window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftcxm2ri730m4uewzaa2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftcxm2ri730m4uewzaa2w.png" alt="Visual Studio Debug dropdown menu containing a Dev Tunnels subitem with a fly out menu showing three menu items: None (active the tunnel), Create A Tunnel..., and Show Dev Tunnels Window." width="500" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Create A Tunnel...&lt;/strong&gt; which opens a dialog to create your tunnel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7regscivf8irgix1lss9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7regscivf8irgix1lss9.png" alt="Dialog with form to create dev tunnel. The form asks for the account to create the tunnel for, in this case a GitHub account, a name for the tunnel, the type which is set to Persistent, and the access level which is set to Public." width="800" height="867"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like the form above, select an &lt;strong&gt;Account&lt;/strong&gt; , enter a &lt;strong&gt;Name&lt;/strong&gt; for the tunnel, set the &lt;strong&gt;Tunnel Type&lt;/strong&gt; to &lt;strong&gt;Persistent&lt;/strong&gt; , and the &lt;strong&gt;Access&lt;/strong&gt; to &lt;strong&gt;Public&lt;/strong&gt;. Then click &lt;strong&gt;OK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you set the Tunnel Type to Persistent, the URL of your dev tunnel is preserved until you delete the tunnel. When set to Temporary, the URL of the tunnel changes whenever you restart Visual Studio. The Tunnel Access has three options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private&lt;/strong&gt; : Only you can access the web application by logging in with your Visual Studio account. This is great for personally testing your application on other devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organizational&lt;/strong&gt; : Anyone within your organization can access the web application. You can use this to demo the application to your teammates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public&lt;/strong&gt; : Anyone can access the web application, and you don't need to log in. Make sure it is safe for you to share your application publicly! &lt;strong&gt;This is what you need to receive HTTP requests from Twilio webhooks.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just like before, Visual Studio will open your web browser and browse to your web application. However, this time it will browse to the public tunnel URL, which looks something like &lt;em&gt;&lt;a href="https://0pbvlk3m-7032.use.devtunnels.ms/" rel="noopener noreferrer"&gt;https://0pbvlk3m-7032.use.devtunnels.ms/&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa6pc552s1kto8fm1d0v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa6pc552s1kto8fm1d0v.png" alt="Browser with public tunnel URL in the URL bar. The URL ends with " width="500" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can open the same URL on any other device and send it to your friends and colleagues. As long as your project is running, they will be able to use the web application that is running locally on your machine.&lt;/p&gt;

&lt;p&gt;Now that your application is publicly available, let's add some Twilio magic to it ☎️&lt;/p&gt;

&lt;h2&gt;
  
  
  Respond to SMS with ASP.NET Core
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Buy a Twilio Phone Number
&lt;/h3&gt;

&lt;p&gt;If you haven't done so already, you'll need to buy a Twilio phone number. To do that, log in to the &lt;a href="https://console.twilio.com/" rel="noopener noreferrer"&gt;Twilio Console&lt;/a&gt;, select &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/active?frameUrl=/console/phone-numbers/incoming" rel="noopener noreferrer"&gt;Phone Numbers&lt;/a&gt;, and then click on the “ &lt;strong&gt;Buy a number&lt;/strong&gt; ” button. Note that if you have a free account, you will be using your trial credit for this purchase.&lt;/p&gt;

&lt;p&gt;On the “ &lt;strong&gt;Buy a Number&lt;/strong&gt; ” page, select your country and check SMS in the “ &lt;strong&gt;Capabilities&lt;/strong&gt; ” field. If you’d like to request a number that is local to your region, you can enter your area code in the “ &lt;strong&gt;Number&lt;/strong&gt; ” field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4990lvhfck2czicjauci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4990lvhfck2czicjauci.png" alt="Buy a Twilio phone number" width="500" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the “ &lt;strong&gt;Search&lt;/strong&gt; ” button to see what numbers are available, and then click “ &lt;strong&gt;Buy&lt;/strong&gt; ” for the number you like from the results. After you confirm your purchase, click the “ &lt;strong&gt;Close&lt;/strong&gt; ” button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle Twilio SMS webhook in ASP.NET Core
&lt;/h3&gt;

&lt;p&gt;Since TwiML is XML, you can use any technique to produce the XML as a response to the Twilio webhook request. However, you can use the &lt;a href="https://www.twilio.com/docs/libraries/csharp-dotnet" rel="noopener noreferrer"&gt;Twilio .NET SDK&lt;/a&gt; and the &lt;a href="https://github.com/twilio-labs/twilio-aspnet" rel="noopener noreferrer"&gt;Twilio helper library for ASP.NET Core&lt;/a&gt; to make it easier.&lt;/p&gt;

&lt;p&gt;First, make sure your application isn't running anymore. You can stop your app by pressing the red square icon in the Visual Studio top toolbar, or close the browser window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fe1lphte5kl4y1tkoyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fe1lphte5kl4y1tkoyj.png" alt="Arrow pointing to the stop button which is a red square in the top toolbar." width="500" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Visual Studio, right-click on the &lt;strong&gt;Dependencies&lt;/strong&gt; node of your project, and click " &lt;strong&gt;Manage NuGet Packages&lt;/strong&gt;". This will open the NuGet Package Manager window in Visual Studio. Click on the &lt;strong&gt;Browse&lt;/strong&gt; tab and search for " &lt;strong&gt;Twilio.AspNet.Core&lt;/strong&gt;". Click on the &lt;strong&gt;Twilio.AspNet.Core&lt;/strong&gt; package by Twilio Labs, and click the &lt;strong&gt;Install&lt;/strong&gt; button to install the latest version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpusi5xbh6n9fovxi82ov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpusi5xbh6n9fovxi82ov.png" alt="NuGet Package Manager window in Visual Studio searching for " width="500" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By installing the Twilio.AspNet.Core package, you also implicitly install the Twilio NuGet package which has the Twilio SDK. You can also explicitly install the Twilio NuGet package to choose the package version yourself.&lt;/p&gt;

&lt;p&gt;Now, replace the code from &lt;em&gt;Program.cs&lt;/em&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.AspNet.Core.MinimalApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;messagingResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ahoy!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Hello World!" GET / endpoint has been replaced with the &lt;em&gt;/message&lt;/em&gt; endpoint. The &lt;em&gt;/message&lt;/em&gt; endpoint creates a &lt;code&gt;MessagingResponse&lt;/code&gt;. &lt;code&gt;messagingResponse.Message("Ahoy!")&lt;/code&gt; will create a Message TwiML verb to respond with "Ahoy!".&lt;br&gt;&lt;br&gt;
Lastly, the &lt;code&gt;Results.Extensions.TwiML&lt;/code&gt; method will create a &lt;code&gt;TwiMLResult&lt;/code&gt; object which takes care of serializing the object to XML and setting the correct content-type headers.&lt;/p&gt;

&lt;p&gt;Start your project again. Visual Studio will open the browser again, but you'll see a HTTP 404 error which is fine, this is expected. Copy the URL somewhere as you'll need it in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure your SMS webhook
&lt;/h3&gt;

&lt;p&gt;Now that your web application is running and publicly available through the tunnel, you can now configure the messaging webhook on your Twilio Phone Number.&lt;/p&gt;

&lt;p&gt;Back in the Twilio Console, navigate to &lt;strong&gt;Phone Numbers &amp;gt; Manage &amp;gt; Active Numbers&lt;/strong&gt; , or &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming?frameUrl=/console/phone-numbers/incoming?x-target-region=us1" rel="noopener noreferrer"&gt;click this link to the Active Numbers page&lt;/a&gt;, then click on your Twilio Phone Number to navigate to the configuration page. Scroll down to the &lt;strong&gt;Messaging&lt;/strong&gt; section and where it says " &lt;strong&gt;A MESSAGE COMES IN&lt;/strong&gt;", pick the " &lt;strong&gt;Webhook&lt;/strong&gt;" option in the first dropdown, paste your Visual Studio tunnel URL and add the &lt;em&gt;/message&lt;/em&gt; path, and then pick the " &lt;strong&gt;HTTP POST&lt;/strong&gt;" option from the next dropdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazz7r485h6zvygeh5clz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazz7r485h6zvygeh5clz.png" alt="Twilio Phone Number Messaging form. There" width="500" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Save&lt;/strong&gt; button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your SMS application
&lt;/h3&gt;

&lt;p&gt;Now that everything has been set up, it's time to test out your SMS application. Pull out your personal phone, and send an SMS to your Twilio Phone Number. You should receive a response saying "Ahoy!".&lt;/p&gt;

&lt;p&gt;One thing I always do to gain a better understanding is to put a breakpoint in the application and see when the breakpoint is hit. Feel free to put a breakpoint inside the &lt;em&gt;/message&lt;/em&gt; endpoint, and play around with the application to see what you can do with Twilio Messaging.&lt;/p&gt;

&lt;p&gt;Alternatively, you can &lt;a href="https://www.twilio.com/blog/test-sms-and-phone-call-applications-with-twilio-dev-phone" rel="noopener noreferrer"&gt;test your messaging and voice applications using the Twilio Dev Phone&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Congratulations on building this SMS application! Since your application is now public, that also means anyone on the internet can reach it which comes with its own risks. To mitigate these risks, you can &lt;a href="https://www.twilio.com/docs/usage/tutorials/how-to-secure-your-csharp-aspnet-core-app-by-validating-incoming-twilio-requests" rel="noopener noreferrer"&gt;validate that the incoming requests originated from Twilio and not some bad actor&lt;/a&gt;. In this tutorial, you manually updated the webhook URL in the Twilio console, but you can also do this programmatically. Check out &lt;a href="https://www.twilio.com/blog/configure-twilio-webhooks-with-visual-studio-dev-tunnels-during-aspdotnet-core-startup" rel="noopener noreferrer"&gt;this follow-up tutorial on how to automatically configure your Twilio webhooks using Visual Studio dev tunnels&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to learn what else you can do with Twilio? Here are some more tutorials for inspiration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/send-vcard-with-mms-using-csharp-and-dotnet" rel="noopener noreferrer"&gt;How to send a vCard with MMS using C# and .NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/how-to-build-an-email-newsletter-application-using-asp-net-core-and-sendgrid" rel="noopener noreferrer"&gt;How to build an Email Newsletter application using ASP.NET Core and SendGrid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/find-us-representatives-congressional-districts-with-sms-aspnetcore" rel="noopener noreferrer"&gt;Find your U.S. Representatives and Congressional Districts with SMS and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/send-emails-with-csharp-handlebars-templating-and-dynamic-email-templates" rel="noopener noreferrer"&gt;Send Emails with C#, Handlebars templating, and Dynamic Email Templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.twilio.com/blog/send-sms-with-csharp-dotnet-and-azure-functions-using-the-twilio-output-binding" rel="noopener noreferrer"&gt;How to send SMS with C# .NET and Azure Functions using the Twilio Output&lt;/a&gt;&lt;a href="https://www.twilio.com/blog/send-sms-with-csharp-dotnet-and-azure-functions-using-the-twilio-output-binding" rel="noopener noreferrer"&gt;Binding&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Shout-out to &lt;a href="https://twitter.com/sayedihashimi" rel="noopener noreferrer"&gt;Sayed I. Hashimi&lt;/a&gt; for answering my questions and taking my feedback.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>visualstudio</category>
    </item>
    <item>
      <title>Build a ChatGPT-like SMS Chatbot with OpenAI and Python</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Thu, 12 Jan 2023 21:48:48 +0000</pubDate>
      <link>https://dev.to/twilio/build-a-chatgpt-like-sms-chatbot-with-openai-and-python-16jn</link>
      <guid>https://dev.to/twilio/build-a-chatgpt-like-sms-chatbot-with-openai-and-python-16jn</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/sms-chatbot-openai-api-node" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://openai.com/blog/chatgpt/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; went viral recently. This conversational machine learning (ML) chatbot developed by &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; can answer questions, admit its mistakes, challenge incorrect premises, generate stories and poetry, and more. Read on to learn how to build a ChatGPT-like SMS chatbot using the OpenAI API and &lt;a href="https://www.twilio.com/docs/sms" rel="noopener noreferrer"&gt;Twilio Programmable Messaging&lt;/a&gt; with Python.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2FirgxCVXa7OgVEXV-BU5YUaLCTMRRIfxAbPmj-Dq0YcMdWneVMlLpOVMx82NKZkc69BhATjjHZ2_Kao" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2FirgxCVXa7OgVEXV-BU5YUaLCTMRRIfxAbPmj-Dq0YcMdWneVMlLpOVMx82NKZkc69BhATjjHZ2_Kao" alt="sms example asking for a haiku about SendGrid to be generated by ChatGPT" width="1024" height="1024"&gt;&lt;/a&gt;&lt;br&gt;
Read &lt;a href="https://www.twilio.com/blog/sms-chatbot-openai-api-node" rel="noopener noreferrer"&gt;this blog post if you'd like to learn how to build the same application but using Node.js&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ChatGPT and Python
&lt;/h3&gt;

&lt;p&gt;GPT-3 (short for “Generative Pre-training Transformer 3”) is a natural language processing (NLP) model trained on human-generated text. Given some text input, it can generate its own human-like text based in a variety of languages and styles. Here, I ask it to "give me some bars about SendGrid."&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2Fe3cwQT1JIJTU_gKl6Y-xYY_th5pO_XJlpYkYQHoaJLurPvS4pKUMjCu9OF2CLOJuBtZBBqMqCU6QTX" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2Fe3cwQT1JIJTU_gKl6Y-xYY_th5pO_XJlpYkYQHoaJLurPvS4pKUMjCu9OF2CLOJuBtZBBqMqCU6QTX" alt="chat example of ChatGPT being asked for some bars about Twilio Segment" width="1024" height="1024"&gt;&lt;/a&gt;&lt;br&gt;
You can &lt;a href="https://chat.openai.com/chat" rel="noopener noreferrer"&gt;test it out in the browser here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The GPT-3 model uses a &lt;a href="https://machinelearningmastery.com/the-transformer-model/" rel="noopener noreferrer"&gt;transformer architecture&lt;/a&gt;. This multi-layer neural network is good for processing sequential data, like text. Language-related tasks it can perform include translation, summarization, and question answering, as well as text generation comparable to human text generation.&lt;/p&gt;

&lt;p&gt;To make a ChatGPT-like application via SMS with Python, you must use the &lt;a href="https://beta.openai.com/dashboard" rel="noopener noreferrer"&gt;OpenAI API&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A Twilio account - &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up for a free one here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Twilio phone number with SMS capabilities - &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;learn how to buy a Twilio Phone Number here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenAI Account – &lt;a href="https://openai.com/api/" rel="noopener noreferrer"&gt;make an OpenAI Account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python installed - &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;download Python here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, a handy utility to connect the development version of our Python application running on your machine to a public URL that Twilio can access. This is needed for the development version of the application because your computer is likely behind a router or firewall, so it isn’t directly reachable on the Internet. You can also choose to &lt;a href="https://www.twilio.com/blog/automating-ngrok-python-twilio-applications-pyngrok" rel="noopener noreferrer"&gt;automate ngrok as shown in this article&lt;/a&gt;.
### Configuration
Since you will be installing some Python packages for this project, you will need to make a new project directory and a &lt;a href="https://docs.python.org/3/tutorial/venv.html" rel="noopener noreferrer"&gt;virtual environment&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're using a Unix or macOS system, open a terminal and enter the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;chatgpt-sms-python
&lt;span class="nb"&gt;cd &lt;/span&gt;chatgpt-sms-python
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;openai twilio flask python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're following this tutorial on Windows, enter the following commands in a command prompt window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;chatgpt-sms-python
&lt;span class="nb"&gt;cd &lt;/span&gt;chatgpt-sms-python
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate
pip &lt;span class="nb"&gt;install &lt;/span&gt;openai twilio flask python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last command uses &lt;code&gt;pip&lt;/code&gt;, the Python package installer, to install the four packages that you are going to use in this project, which are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;a href="https://pypi.org/project/openai/" rel="noopener noreferrer"&gt;OpenAI Python client library&lt;/a&gt;, to send requests to OpenAI's &lt;a href="https://www.fullstackpython.com/gpt-3.html" rel="noopener noreferrer"&gt;GPT-3&lt;/a&gt; engine.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.twilio.com/docs/libraries/python" rel="noopener noreferrer"&gt;Twilio Python Helper library&lt;/a&gt;, to work with SMS messages.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.palletsprojects.com/p/flask/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; framework, to create the web application in Python.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://pypi.org/project/python-dotenv/" rel="noopener noreferrer"&gt;python-dotenv&lt;/a&gt; package, to read a configuration file.
As mentioned above, this project needs an OpenAI API Key. After making an OpenAI account, you can &lt;a href="https://beta.openai.com/account/api-keys" rel="noopener noreferrer"&gt;get an OpenAI API Key here&lt;/a&gt; by clicking on &lt;strong&gt;+ Create new secret key&lt;/strong&gt;.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a1cyh6ppewz6morkhuj.png" alt="openAI API keys" width="800" height="262"&gt;
The Python application will need to have access to this key, so we are going to make a .env file where the API key can safely be stored. The application we create will be able to import this key as an environment variable soon.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create a .env file in your project’s root directory and enter the following line of text, making sure to replace  with your actual key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &amp;lt;YOUR-OPENAI-KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that the &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; is safe and that you don't expose your &lt;em&gt;.env&lt;/em&gt; file in a public location such as GitHub.&lt;/p&gt;

&lt;p&gt;Now, your Flask app will need to be visible from the web so Twilio can send requests to it. ngrok lets you do this. With ngrok installed, run &lt;code&gt;ngrok http 5000&lt;/code&gt; in a new terminal tab in the directory your code is in.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz97kiwf1e89ofvkk31r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz97kiwf1e89ofvkk31r.png" alt="ngrok in terminal showing URLs generated to copy and paste to configure twilio phone #" width="624" height="308"&gt;&lt;/a&gt;&lt;br&gt;
You should see the screen above. Grab that ngrok Forwarding URL to configure your Twilio number: select your Twilio number under &lt;strong&gt;Active Numbers&lt;/strong&gt; in your Twilio &lt;a href="https://www.twilio.com/console/phone-numbers/incoming" rel="noopener noreferrer"&gt;console&lt;/a&gt;, scroll to the Messaging section, and then modify the phone number’s routing by pasting the ngrok URL in the textbox corresponding to when &lt;em&gt;A Message Comes In&lt;/em&gt; as shown below:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j10ulzaxi17r7kjo7dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j10ulzaxi17r7kjo7dy.png" alt="phone number configuration in the console under where a message comes in" width="800" height="296"&gt;&lt;/a&gt;&lt;br&gt;
Click &lt;strong&gt;Save&lt;/strong&gt; and now your Twilio Phone Number is configured so that it maps to your web application server running locally on your computer. Let's build that application now.&lt;/p&gt;
&lt;h3&gt;
  
  
  Build your ChatGPT-like SMS Python App
&lt;/h3&gt;

&lt;p&gt;Inside your &lt;em&gt;chatgpt-sms-python&lt;/em&gt; directory, make a new file called &lt;em&gt;app.py&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Copy and paste the following code into &lt;em&gt;app.py&lt;/em&gt; to start off the ChatGPT-like SMS app. It imports the required modules, sets the OpenAI API key from the &lt;em&gt;.env&lt;/em&gt; file, and creates a Flask app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;twilio.twiml.messaging_response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MessagingResponse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;
&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Directly beneath, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/sms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chatgpt&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;get incoming message&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;inb_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inb_msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-davinci-003&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inb_msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Respond to incoming calls with a simple text message.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Start our TwiML response
&lt;/span&gt;    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Add a message
&lt;/span&gt;    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside of the &lt;code&gt;/sms&lt;/code&gt; webhook, this code creates a variable &lt;code&gt;inbMsg&lt;/code&gt; from the inbound text message users will text in and prints it out. It then calls the &lt;code&gt;openai.Completion.create&lt;/code&gt; method to use one of their language models to generate text based on &lt;code&gt;inbMsg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order to specify our completion, you pass in some properties: &lt;code&gt;model&lt;/code&gt; identifies the OpenAI language model used to generate an answer for the text which we assign to the &lt;code&gt;prompt&lt;/code&gt; property. In this tutorial, you use the &lt;code&gt;text-davinci-003&lt;/code&gt; language model. It's the same language model used in the background by ChatGPT. The OpenAI docs list the other language models they offer for use.&lt;/p&gt;

&lt;p&gt;Optional properties &lt;code&gt;max_tokens&lt;/code&gt; and &lt;code&gt;frequency_penalty&lt;/code&gt; specify the maximum completion length and the effort the model will make to not repeat itself. You can see more &lt;a href="https://beta.openai.com/docs/api-reference/completions/create" rel="noopener noreferrer"&gt;optional properties for completion here in the OpenAI documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/elizabethsiegle/chatgpt-sms-python" rel="noopener noreferrer"&gt;view the complete app on GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Run the Python Flask app by running the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now take out your phone and text your Twilio Phone Number a question or prompt so that OpenAI can answer it or generate text!&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnitdf13rtf1uivijvkl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnitdf13rtf1uivijvkl8.png" alt="sms example asking chatGPT for a haiku about SendGrid as well as the questions did humans invent or discover mathematics" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for Twilio and ChatGPT?
&lt;/h3&gt;

&lt;p&gt;The development possibilities offered by ChatGPT and Twilio are endless! You can build an &lt;a href="https://www.twilio.com/blog/openai-gpt-3-chatbot-python-twilio-sms" rel="noopener noreferrer"&gt;SMS chatbot with Python&lt;/a&gt;, &lt;a href="https://www.twilio.com/blog/call-ai-friend-gpt-3-twilio-voice-functions" rel="noopener noreferrer"&gt;call an AI friend&lt;/a&gt;, &lt;a href="https://www.twilio.com/blog/python-whatsapp-chef-bot-openai-gpt3" rel="noopener noreferrer"&gt;chat with an AI chef over WhatsApp&lt;/a&gt;, use the &lt;a href="https://www.twilio.com/blog/sms-chatbot-openai-api-node" rel="noopener noreferrer"&gt;OpenAI API with Node.js and Twilio Serverless&lt;/a&gt;, and more. Let me know what you're working on with OpenAI or Python–I can't wait to see what you build.&lt;/p&gt;

</description>
      <category>python</category>
      <category>openai</category>
      <category>chatgpt</category>
      <category>ngrok</category>
    </item>
    <item>
      <title>Build a Serverless ChatGPT SMS Chatbot with the OpenAI API</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Mon, 09 Jan 2023 20:48:02 +0000</pubDate>
      <link>https://dev.to/twilio/build-a-serverless-chatgpt-sms-chatbot-with-the-openai-api-3am</link>
      <guid>https://dev.to/twilio/build-a-serverless-chatgpt-sms-chatbot-with-the-openai-api-3am</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/sms-chatbot-openai-api-node" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openai.com/blog/chatgpt/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; went viral recently! This conversational machine learning (ML) chatbot developed by &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; can answer questions, admit its mistakes, challenge incorrect premises, generate stories and poetry, and more. Read on to learn how to build a serverless SMS chatbot using ChatGPT and the OpenAI API, &lt;a href="https://www.twilio.com/docs/sms" rel="noopener noreferrer"&gt;Twilio Programmable Messaging&lt;/a&gt;, the &lt;a href="https://www.twilio.com/docs/labs/serverless-toolkit" rel="noopener noreferrer"&gt;Twilio Serverless Toolkit&lt;/a&gt;, and Node.js.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2FjUvA2cr6auC88298l8MJwVN_9ZpmI98Wkb80gt4-yflquLL-pl8YPI5_3PUxraJVnurWy91KsOa_KE" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2FjUvA2cr6auC88298l8MJwVN_9ZpmI98Wkb80gt4-yflquLL-pl8YPI5_3PUxraJVnurWy91KsOa_KE" alt="gif example of SMS version of ChatGPT powered by Twilio with the OpenAI API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can test this chatbot out yourself by texting a question or prompt (like in the GIF above) to &lt;strong&gt;+17622490430&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Do you prefer learning via video more? Check out this &lt;a href="https://www.tiktok.com/@lizziepikachu/video/7185762349775621419" rel="noopener noreferrer"&gt;TikTok summarizing this tutorial&lt;/a&gt; in under three minutes!&lt;br&gt;
&lt;a href="https://www.tiktok.com/embed/7185762349775621419" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F8932430%2F211401718-be0abb93-92d7-43a0-a962-309478750f88.png" alt="tiktok versiont"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  ChatGPT and Node.js
&lt;/h3&gt;

&lt;p&gt;GPT-3 (short for “Generative Pre-training Transformer 3”) is a natural language processing (NLP) model trained on human-generated text. Given some text input, it can generate its own human-like text based in a variety of languages and styles. Here, I ask it to "give me some bars about SendGrid."&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2F_KfeYfEcgH8O8RaiYJERqMEHTlDIYjPIhZ-aCXfZIASdlpoC9M44fZly2_TrFWC0bgxQ93gepbVqY8" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Foriginal_images%2F_KfeYfEcgH8O8RaiYJERqMEHTlDIYjPIhZ-aCXfZIASdlpoC9M44fZly2_TrFWC0bgxQ93gepbVqY8" alt="gif of chatgpt in browser"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://chat.openai.com/chat" rel="noopener noreferrer"&gt;test out ChatGPT in the browser here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The GPT-3 model uses a &lt;a href="https://machinelearningmastery.com/the-transformer-model/" rel="noopener noreferrer"&gt;transformer architecture&lt;/a&gt;. This multi-layer neural network is good for processing sequential data, like text. Language-related tasks it can perform include translation, summarization, and question answering, as well as text generation comparable to human text generation.&lt;/p&gt;

&lt;p&gt;To use ChatGPT in a Node.js application, you must use the &lt;a href="https://beta.openai.com/dashboard" rel="noopener noreferrer"&gt;OpenAI API&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A Twilio account - sign up for a free &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;Twilio Account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Twilio Phone Number with SMS capabilities - &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;learn how to buy a Twilio Phone Number here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenAI Account – &lt;a href="https://openai.com/api/" rel="noopener noreferrer"&gt;make an OpenAI Account here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Node.js installed - &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;download Node.js here&lt;/a&gt;
### Get Started with OpenAI
After making an OpenAI account, you'll need an API Key. You can &lt;a href="https://beta.openai.com/account/api-keys" rel="noopener noreferrer"&gt;get an OpenAI API Key here&lt;/a&gt; by clicking on &lt;strong&gt;+ Create new secret key&lt;/strong&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2F1lHXDS_s-kB9Px2SGTFGmG8t57burs6xgiF0RAJsq9-wR.width-1600.png" alt="api keys screenshot"&gt;
Save that API key for later to use the OpenAI client library in your Twilio Function.
### Get Started with the Twilio Serverless Toolkit
The Serverless Toolkit is CLI tooling that helps you develop Twilio Functions locally and deploy them to &lt;a href="https://www.twilio.com/docs/serverless/functions-assets" rel="noopener noreferrer"&gt;Twilio Functions &amp;amp; Assets&lt;/a&gt;. The best way to work with the Serverless Toolkit is through the Twilio CLI. If you don't have the &lt;a href="https://www.twilio.com/docs/twilio-cli/quickstart" rel="noopener noreferrer"&gt;Twilio CLI&lt;/a&gt; installed yet, run the following commands on the command line to install it and the Serverless Toolkit:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;twilio-cli &lt;span class="nt"&gt;-g&lt;/span&gt;
twilio login
twilio plugins:install @twilio-labs/plugin-serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Afterwards, create your new project and install our lone requirement &lt;a href="https://www.npmjs.com/package/openai" rel="noopener noreferrer"&gt;&lt;code&gt;openai&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;twilio serverless:init chatgpt-sms &lt;span class="nt"&gt;--template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;blank
&lt;span class="nb"&gt;cd &lt;/span&gt;chatgpt-sms
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; openai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set an Environment Variable with Twilio Functions and Assets
&lt;/h3&gt;

&lt;p&gt;Open up your .env file for your Functions project in your root directory and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR-OPENAI-API-KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR-OPENAI-API-KEY&lt;/code&gt; with the OpenAI API Key you took note off earlier. Now you can access this API Key if you'd like to do so in your code with &lt;code&gt;context.OPENAI_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make a Twilio Function with JavaScript
&lt;/h3&gt;

&lt;p&gt;Make a new file in the &lt;code&gt;/functions directory&lt;/code&gt; called &lt;code&gt;chatgpt.js&lt;/code&gt; containing the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OpenAIApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;twiml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbMsg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAIApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-davinci-003&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbMsg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//A number between 0 and 1 that determines how many creative risks the engine takes when generating text.&lt;/span&gt;
      &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Maximum completion length. max: 4000-prompt&lt;/span&gt;
      &lt;span class="na"&gt;frequency_penalty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="c1"&gt;// # between 0 and 1. The higher this value, the bigger the effort the model will make in not repeating itself.&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code imports &lt;code&gt;openAI&lt;/code&gt; and makes an async function containing a &lt;a href="https://www.twilio.com/docs/sms/api/message-resource" rel="noopener noreferrer"&gt;Twilio Messaging Response object&lt;/a&gt; and a variable &lt;code&gt;inbMsg&lt;/code&gt; from the inbound text message users will text in. It then initializes a &lt;code&gt;Configuration&lt;/code&gt; object and passes it an object to the &lt;code&gt;Configuration&lt;/code&gt; constructor containing the property &lt;code&gt;apiKey&lt;/code&gt;. The function then calls the &lt;code&gt;openai.createCompletion&lt;/code&gt; function to use one of their language models to generate text based on &lt;code&gt;inbMsg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order to specify our completion, you need to pass in a configuration object containing two properties: &lt;code&gt;model&lt;/code&gt; and &lt;code&gt;prompt&lt;/code&gt;. &lt;code&gt;model&lt;/code&gt; identifies the OpenAI language model used to generate an answer for the text which we assign to the &lt;code&gt;prompt&lt;/code&gt; property. In this tutorial, you'll use the &lt;code&gt;text-davinci-003&lt;/code&gt; language model. It's the same language model used in the background by ChatGPT. The OpenAI docs list the &lt;a href="https://beta.openai.com/docs/models/overview" rel="noopener noreferrer"&gt;other language models they offer for use&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Optional properties &lt;code&gt;max_tokens&lt;/code&gt; and &lt;code&gt;frequency_penalty&lt;/code&gt; specify the maximum completion length and the effort the model will make to not repeat itself. You can see more &lt;a href="https://beta.openai.com/docs/api-reference/completions/create" rel="noopener noreferrer"&gt;optional properties for completion here in the OpenAI documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can view the &lt;a href="https://github.com/elizabethsiegle/chatGPT-SMS" rel="noopener noreferrer"&gt;complete source code on GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Function with a Twilio Phone Number
&lt;/h3&gt;

&lt;p&gt;To deploy your app to Twilio, run &lt;code&gt;twilio serverless:deploy&lt;/code&gt; from the &lt;em&gt;chatgpt-sms&lt;/em&gt; root directory. You should see the URL of your Function at the bottom of your terminal:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FfbQL11tAB_kdnOAog1aPiKLcLqIWNOox55CfMg6cK4m02.width-1000.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2FfbQL11tAB_kdnOAog1aPiKLcLqIWNOox55CfMg6cK4m02.width-1000.png" alt="deployed function with urls"&gt;&lt;/a&gt;&lt;br&gt;
Grab the Function URL corresponding to your app (the one that ends with &lt;em&gt;/chatgpt&lt;/em&gt;) and &lt;a href="https://www.twilio.com/console/phone-numbers/incoming" rel="noopener noreferrer"&gt;configure a Twilio Phone Number&lt;/a&gt; with it as shown below: select the Twilio number you just purchased in your &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming" rel="noopener noreferrer"&gt;Twilio Phone Numbers console&lt;/a&gt; and scroll down to the &lt;em&gt;Messaging&lt;/em&gt; section. Paste the link in the text field for &lt;strong&gt;A MESSAGE COMES IN&lt;/strong&gt; webhook making sure that it's set to &lt;strong&gt;HTTP POST&lt;/strong&gt;. When you click &lt;strong&gt;Save&lt;/strong&gt;, it should look like this!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2Fr1ViXfhX01xjrXXa528-OynIzwLtOQxFWlX78CN4-T3IE.width-1000.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2Fr1ViXfhX01xjrXXa528-OynIzwLtOQxFWlX78CN4-T3IE.width-1000.png" alt="twilio phone number configuration in console"&gt;&lt;/a&gt;&lt;br&gt;
The &lt;a href="https://www.twilio.com/docs/serverless/functions-assets/functions/create-service" rel="noopener noreferrer"&gt;Service&lt;/a&gt; is the Serverless project name, environment provides no other options, and Function Path is the file name. Now take out your phone and text a question or prompt to your Twilio number.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2Fsc0jURdMOhnzjwhlToJRWvAltGFwke7shO8VIXNBB8pTo.width-1000.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.cdn.prod.twilio.com%2Fimages%2Fsc0jURdMOhnzjwhlToJRWvAltGFwke7shO8VIXNBB8pTo.width-1000.jpg" alt="sms example of chatgpt over sms answering how to get to machu picchu from sfo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for Twilio Serverless and ChatGPT?
&lt;/h3&gt;

&lt;p&gt;The development possibilities offered by ChatGPT and Twilio are endless! You can build an &lt;a href="https://www.twilio.com/blog/openai-gpt-3-chatbot-python-twilio-sms" rel="noopener noreferrer"&gt;SMS chatbot with Python&lt;/a&gt;, &lt;a href="https://www.twilio.com/blog/call-ai-friend-gpt-3-twilio-voice-functions" rel="noopener noreferrer"&gt;call an AI friend&lt;/a&gt;, &lt;a href="https://www.twilio.com/blog/python-whatsapp-chef-bot-openai-gpt3" rel="noopener noreferrer"&gt;chat with an AI chef over WhatsApp&lt;/a&gt;, and more. Let me know what you're working on with OpenAI–I can't wait to see what you build.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>openai</category>
      <category>chatgpt</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Pull Congressional Data via SMS with the Congress API and JavaScript</title>
      <dc:creator>Lizzie Siegle</dc:creator>
      <pubDate>Mon, 31 Oct 2022 23:27:27 +0000</pubDate>
      <link>https://dev.to/twilio/pull-congressional-data-via-sms-with-the-congress-api-and-javascript-43bf</link>
      <guid>https://dev.to/twilio/pull-congressional-data-via-sms-with-the-congress-api-and-javascript-43bf</guid>
      <description>&lt;p&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/congress-api-sms" rel="noopener noreferrer"&gt;originally published on the Twilio blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Congress recently came out with an official API so the public can "view, retrieve, and re-use machine-readable data from collections available on Congress.gov!" Read on to learn how to read congressional data using the &lt;a href="https://api.congress.gov/" rel="noopener noreferrer"&gt;Congress API&lt;/a&gt;, &lt;a href="https://www.twilio.com/docs/runtime/functions" rel="noopener noreferrer"&gt;Twilio Functions&lt;/a&gt;, and the &lt;a href="https://www.twilio.com/docs/labs/serverless-toolkit" rel="noopener noreferrer"&gt;Twilio Serverless Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Text "bill", "amendment", or "summaries" to &lt;strong&gt;+12029337044&lt;/strong&gt; to receive data about a random bill or amendment!&lt;/em&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lc9XW2D5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/HYVHmE_W9Y8knYpQ9QYbRs1ZmWT5wHog1u6cqJqNC4xzG.width-1000.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lc9XW2D5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/HYVHmE_W9Y8knYpQ9QYbRs1ZmWT5wHog1u6cqJqNC4xzG.width-1000.jpg" alt="sms example" width="739" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A Twilio account - &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up for a free one here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Twilio phone number with SMS capabilities - &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;configure one here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Congress.gov API Key – &lt;a href="https://api.congress.gov/sign-up/" rel="noopener noreferrer"&gt;get one here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.postman.com/downloads/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; (you could alternatively make cURL requests from the command line)&lt;/li&gt;
&lt;li&gt;Node.js installed - &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;download it here&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Make a congress.gov Request
&lt;/h3&gt;

&lt;p&gt;You can look over the &lt;a href="https://api.congress.gov/#/" rel="noopener noreferrer"&gt;different Congress API endpoints offered here&lt;/a&gt;. To get someone's profile information, the URL would look like &lt;code&gt;[https://api.congress.gov/v3/bill/117/s/4693/summaries](https://api.congress.gov/v3/bill/117/s/4693/summaries)&lt;/code&gt; where 117 is the [Congress number&lt;a href="https://en.wikipedia.org/wiki/117th_United_States_Congress" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/117th_United_States_Congress&lt;/a&gt;, "s" is the type of bill, and "4693" is the bill number. Open Postman and paste that URL into the URL bar.&lt;/p&gt;

&lt;p&gt;Add your API Key under Params: the &lt;code&gt;key&lt;/code&gt; is &lt;code&gt;api_key&lt;/code&gt; and the &lt;code&gt;value&lt;/code&gt; is your Congress API Key.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Send&lt;/code&gt; to hit it with a GET request to see the following data returned:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KpvgQVk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Screen_Shot_2022-10-29_at_3.04.38_PM.width-1600.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KpvgQVk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Screen_Shot_2022-10-29_at_3.04.38_PM.width-1600.png" alt="congress API data returned in Postman" width="800" height="491"&gt;&lt;/a&gt;&lt;br&gt;
Again, you can see &lt;a href="https://api.congress.gov/" rel="noopener noreferrer"&gt;more Congress API endpoints here you can play around with&lt;/a&gt;!&lt;/p&gt;
&lt;h3&gt;
  
  
  Get Started with the Twilio Serverless Toolkit
&lt;/h3&gt;

&lt;p&gt;The Serverless Toolkit is CLI tooling that helps you develop locally and deploy to &lt;a href="https://www.twilio.com/docs/serverless/functions-assets" rel="noopener noreferrer"&gt;Twilio Functions &amp;amp; Assets&lt;/a&gt;. The best way to work with the Serverless Toolkit is through the &lt;a href="https://www.twilio.com/docs/twilio-cli/quickstart" rel="noopener noreferrer"&gt;Twilio CLI&lt;/a&gt;. If you don't have the Twilio CLI installed yet, run the following commands on the command line to install it and the Serverless Toolkit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;twilio-cli &lt;span class="nt"&gt;-g&lt;/span&gt;
twilio login
twilio plugins:install @twilio-labs/plugin-serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, create your new project and install our lone requirement &lt;code&gt;[undici](https://github.com/nodejs/undici)&lt;/code&gt; to make HTTP requests in Node.js by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;twilio serverless:init congress-api-sms –template&lt;span class="o"&gt;=&lt;/span&gt;blank
&lt;span class="nb"&gt;cd &lt;/span&gt;congress-api-sms
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; undici
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Functions and Assets team recommends using &lt;code&gt;undici&lt;/code&gt; with your Functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set an Environment Variable with Twilio Functions and Assets
&lt;/h3&gt;

&lt;p&gt;Open up your &lt;code&gt;.env&lt;/code&gt; file for your Functions project in your root directory and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONGRESS_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR-CONGRESS-API-KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can access this API Key if you'd like to do so in your code with &lt;code&gt;context.CONGRESS_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make a Twilio Function with JavaScript
&lt;/h3&gt;

&lt;p&gt;Make a new file in the &lt;code&gt;/functions&lt;/code&gt; directory called &lt;em&gt;congress.js&lt;/em&gt; containing the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undici&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbMsg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;twiml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inbMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bill&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;body&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.congress.gov/v3/bill?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONGRESS_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billsLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;randNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;billsLength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bills&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;randNum&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;latestAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originChamber&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bill&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;HR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;house bill&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;senate bill&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;HJRES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;house joint resolution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;SJRES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;senate joint resolution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;HCONRES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;house concurrent resolution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;SCONRES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;senate concurrent resolution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;HRES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;house simple resolution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;SRES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;senate simple resolution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Bill: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.\nType: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;billMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;\nAssigned bill or resolution number:   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nIt originated in the &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;originChamber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; but the latest action was &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latestAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latestAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actionDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inbMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;amendment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;body&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.congress.gov/v3/amendment?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONGRESS_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amendmentLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amendments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;randNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amendmentLength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amendment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amendments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;randNum&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;latestAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;congress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amendment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Amendment purpose: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nAssigned amendment or resolution number:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n Latest action was on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latestAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actionDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latestAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.\n Congress: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;congress&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inbMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summaries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CURRENT_CONGRESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;body&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.congress.gov/v3/summaries/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CURRENT_CONGRESS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONGRESS_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sumLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;randNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sumLength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;randNum&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentChamber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actionDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actionDesc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;regexToStripHtmlTags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.|&lt;/span&gt;&lt;span class="se"&gt;\n)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textWithoutHtmlTags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;regexToStripHtmlTags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Summary title: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.\nStarted in: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originChamber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;actionDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, currently in the &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentChamber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; chamber in Congress &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;congress&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and it was &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;actionDesc&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;textWithoutHtmlTags&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Send "bill", "amendment", or "summaries"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;twiml&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code imports &lt;code&gt;undici&lt;/code&gt;, makes a &lt;a href="https://www.twilio.com/docs/sms/api/message-resource" rel="noopener noreferrer"&gt;Twilio Messaging Response object&lt;/a&gt;, creates a variable &lt;code&gt;inbMsg&lt;/code&gt; from the inbound text message users will text in and checks if that message input contains &lt;code&gt;bill&lt;/code&gt;, &lt;code&gt;amendment&lt;/code&gt;, or &lt;code&gt;summaries&lt;/code&gt; (three endpoints of the Congress API). The code then generates a random number based on the total amount of items returned from the Congress API to select a random Bill or Amendment, parses the responses from that given endpoint (as we saw using Postman above) into variables such as the type of Amendment or Bill, the most recent date some action was taken, and more!&lt;/p&gt;

&lt;p&gt;If someone does not send a message containing "bill", "summaries", or "amendment" (the three topics they can learn about), we return a message saying so. Else, we parse the Congress object returned for information such as the latest action taken on a bill, type of bill, purpose of the given random amendment, origin chamber of a bill, and more. We return a text message containing that data!&lt;/p&gt;

&lt;p&gt;You can view the &lt;a href="https://github.com/elizabethsiegle/congress-api-sms-serverless" rel="noopener noreferrer"&gt;complete app on GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Function with a Twilio Phone Number
&lt;/h3&gt;

&lt;p&gt;To open up our app to the web with a public-facing URL, run &lt;code&gt;twilio serverless:deploy&lt;/code&gt; from the &lt;code&gt;congress-api-sms&lt;/code&gt; root directory. You should see this at the bottom of your terminal:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l6_x-gp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/vRoJgBk9ankJswPRzdI7pB9d4Z_bDg7k8VqGl553E1xte.width-1000.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l6_x-gp9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/vRoJgBk9ankJswPRzdI7pB9d4Z_bDg7k8VqGl553E1xte.width-1000.png" alt="twilio function urls once deployed" width="800" height="346"&gt;&lt;/a&gt;&lt;br&gt;
Grab the Function URL corresponding to your app (the one that ends with &lt;code&gt;/congress&lt;/code&gt;) and configure a Twilio phone number with it as shown below: select the Twilio number you just purchased in your Twilio phone numbers console and scroll down to the &lt;em&gt;Messaging&lt;/em&gt; section. Paste the link in the text field for &lt;em&gt;A MESSAGE COMES IN&lt;/em&gt; webhook making sure that it's set to &lt;em&gt;HTTP POST&lt;/em&gt;. When you click &lt;em&gt;Save&lt;/em&gt; it should look like this!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lUjUvEw4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/VpL0FI8mnh3kr34peiPb84tWuvxnW3apxB9qAKrDcC90F.width-1000.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lUjUvEw4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/VpL0FI8mnh3kr34peiPb84tWuvxnW3apxB9qAKrDcC90F.width-1000.png" alt="messaging section of twilio phone number" width="800" height="508"&gt;&lt;/a&gt;&lt;br&gt;
The Service is the Serverless project name, environment provides no other options, and Function Path is the file name. Now take out your phone and text "bill" or "amendment" or "summaries" to your Twilio number.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uCzup_Ij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/MP41e2iatOLCc2VsbIoOVxzEXTKL9OEPW1JQOpZw9OWNe.width-1000.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uCzup_Ij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/MP41e2iatOLCc2VsbIoOVxzEXTKL9OEPW1JQOpZw9OWNe.width-1000.jpg" alt="sms example" width="739" height="1600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for Twilio Serverless and the Congress API?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oC9RVRtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/original_images/XPsVwWwntVmcz3LF9bTOJC2Cv4vgEfXpYRFd6bI7mNqwMUNbJNiPbsvkh0BoEFcgrkkP4pyQIiucSG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oC9RVRtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/original_images/XPsVwWwntVmcz3LF9bTOJC2Cv4vgEfXpYRFd6bI7mNqwMUNbJNiPbsvkh0BoEFcgrkkP4pyQIiucSG" alt="being in congress must be fun kimmy schmidt gif" width="480" height="270"&gt;&lt;/a&gt;&lt;br&gt;
Twilio's Serverless Toolkit makes it possible to deploy web apps quickly, and Functions &amp;amp; Assets seamlessly handles servers for you. You can do a lot with this data, and you can see what actions Congress are taking--it's fun data to play around with! Let me know online what you're building with Twilio Functions&amp;amp;Assets and don't forget to vote this November!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>serverless</category>
      <category>twilio</category>
    </item>
  </channel>
</rss>
