
<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Shiny</title>
<link>https://shiny.posit.co/blog/</link>
<atom:link href="https://shiny.posit.co/blog/index.xml" rel="self" type="application/rss+xml"/>
<description>News, tips, and commentary about all things Shiny</description>
<image>
<url>https://shiny.posit.co/images/shiny-thumb.png</url>
<title>Shiny</title>
<link>https://shiny.posit.co/blog/</link>
</image>
<generator>quarto-1.4.557</generator>
<lastBuildDate>Thu, 02 Apr 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Shiny for Python 1.6 brings toolbars and OpenTelemetry</title>
  <dc:creator>Liz Nelson, Barret Schloerke</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-python-1.6/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-python-1.6/py-shiny-otel-toolbars.png" alt="Shiny for Python 1.6 brings toolbars and OpenTelemetry" style="width: 100%; height: auto;">
    </div>
    


<style>
  .panel-tabset .tab-content, .nav {
    border: none;
  }
  .panel-tabset.nav-centered .nav {
    justify-content: center;
  }
</style>
<p>We’re pleased to announce that Shiny for Python <code>v1.6</code> is now <a href="https://pypi.org/project/shiny/">available on PyPI</a>!</p>
<p>Install it now with <code>pip install -U shiny</code>.</p>
<p>This release has two big additions: toolbar components for building compact, modern UIs, and OpenTelemetry support for understanding how your apps behave in production. A full list of changes is available in the <a href="https://github.com/posit-dev/py-shiny/blob/main/CHANGELOG.md">CHANGELOG</a>.</p>
<section id="toolbars" class="level2">
<h2 data-anchor-id="toolbars">Toolbars</h2>
<p>Toolbars are a new set of compact components designed to fit controls into tight spaces — card headers and footers, input labels, and text areas. They’re perfect for dashboards that are running out of room, or for AI chat interfaces where you want to add controls without cluttering the layout.</p>
<p>The core components are:</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>Component</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>ui.toolbar()</code></td>
<td>Container for toolbar inputs</td>
</tr>
<tr class="even">
<td><code>ui.toolbar_input_button()</code></td>
<td>A small action button</td>
</tr>
<tr class="odd">
<td><code>ui.toolbar_input_select()</code></td>
<td>A compact dropdown select</td>
</tr>
<tr class="even">
<td><code>ui.toolbar_divider()</code></td>
<td>A visual separator</td>
</tr>
<tr class="odd">
<td><code>ui.toolbar_spacer()</code></td>
<td>Pushes items to opposite sides</td>
</tr>
</tbody>
</table>
<p>Each input also has a corresponding <code>ui.update_toolbar_input_*()</code> function for updating it dynamically.</p>
<section id="toolbars-in-card-headers-and-footers" class="level3">
<h3 data-anchor-id="toolbars-in-card-headers-and-footers">Toolbars in card headers and footers</h3>
<p>The most common use case is placing a toolbar in a card header to attach controls directly to a card’s content:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.6/toolbar_ex1.png" class="img-fluid figure-img"></p>
<figcaption>Example of a card with a toolbar input button and toolbar input select</figcaption>
</figure>
</div>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> faicons <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> icon_svg</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, render, ui</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.card(full_screen<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>):</span>
<span id="cb1-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.card_header():</span>
<span id="cb1-6">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"Header"</span></span>
<span id="cb1-7">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.toolbar(align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>):</span>
<span id="cb1-8">            ui.toolbar_input_button(</span>
<span id="cb1-9">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"action1"</span>,</span>
<span id="cb1-10">                label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Refresh"</span>,</span>
<span id="cb1-11">                icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"arrows-rotate"</span>),</span>
<span id="cb1-12">            )</span>
<span id="cb1-13">            ui.toolbar_divider()</span>
<span id="cb1-14">            ui.toolbar_input_select(</span>
<span id="cb1-15">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"options"</span>,</span>
<span id="cb1-16">                label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Filter"</span>,</span>
<span id="cb1-17">                choices<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ABC"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CDE"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"EFG"</span>],</span>
<span id="cb1-18">            )</span>
<span id="cb1-19"></span>
<span id="cb1-20">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb1-21">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> toolbar_status():</span>
<span id="cb1-22">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Button clicks: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>action1()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, Selected: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>options()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span></code></pre></div>
</section>
<section id="toolbars-in-input-labels" class="level3">
<h3 data-anchor-id="toolbars-in-input-labels">Toolbars in input labels</h3>
<p>You can also pass a toolbar as an input’s <code>label</code> to add an info button for additional information or provide quick actions, like resetting an input value.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.6/toolbar_info_ex.png" class="img-fluid figure-img"></p>
<figcaption>Example of an info toolbar button using a tooltip in an input label</figcaption>
</figure>
</div>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> faicons <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> icon_svg</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ui</span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.card():</span>
<span id="cb2-5">    ui.card_header(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Data Settings"</span>)</span>
<span id="cb2-6">    ui.input_slider(</span>
<span id="cb2-7">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"threshold"</span>,</span>
<span id="cb2-8">        label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.toolbar(</span>
<span id="cb2-9">            ui.toolbar_input_button(</span>
<span id="cb2-10">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"threshold_info"</span>,</span>
<span id="cb2-11">                label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"About this setting"</span>,</span>
<span id="cb2-12">                icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"circle-info"</span>),</span>
<span id="cb2-13">                tooltip<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Standard deviations from the mean before a value is flagged as an outlier."</span>,</span>
<span id="cb2-14">            ),</span>
<span id="cb2-15">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Outlier threshold"</span>,</span>
<span id="cb2-16">            align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"left"</span>,</span>
<span id="cb2-17">        ),</span>
<span id="cb2-18">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb2-19">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>,</span>
<span id="cb2-20">        value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb2-21">        step<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>,</span>
<span id="cb2-22">    )</span>
<span id="cb2-23">    ui.input_numeric(</span>
<span id="cb2-24">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sample_size"</span>,</span>
<span id="cb2-25">        label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.toolbar(</span>
<span id="cb2-26">            ui.toolbar_input_button(</span>
<span id="cb2-27">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sample_info"</span>,</span>
<span id="cb2-28">                label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"About this setting"</span>,</span>
<span id="cb2-29">                icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"circle-info"</span>),</span>
<span id="cb2-30">                tooltip<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Number of observations to draw from the dataset for each analysis run."</span>,</span>
<span id="cb2-31">            ),</span>
<span id="cb2-32">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Sample size"</span>,</span>
<span id="cb2-33">            align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"left"</span>,</span>
<span id="cb2-34">        ),</span>
<span id="cb2-35">        value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>,</span>
<span id="cb2-36">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb2-37">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span>,</span>
<span id="cb2-38">        step<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb2-39">    )</span></code></pre></div>
</section>
<section id="toolbars-in-text-areas" class="level3">
<h3 data-anchor-id="toolbars-in-text-areas">Toolbars in text areas</h3>
<p>The <code>input_submit_textarea()</code> component accepts a <code>toolbar</code> parameter directly, making it easy to add contextual controls for AI chat interfaces and message composers:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.6/toolbar_textarea.png" class="img-fluid figure-img"></p>
<figcaption>Example of a toolbar in an input submit textarea</figcaption>
</figure>
</div>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> faicons <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> icon_svg</span>
<span id="cb3-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> reactive</span>
<span id="cb3-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, render, ui</span>
<span id="cb3-4"></span>
<span id="cb3-5">ui.page_opts(fillable<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb3-6"></span>
<span id="cb3-7">messages <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> reactive.value([])</span>
<span id="cb3-8"></span>
<span id="cb3-9"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.card(full_screen<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"250px"</span>):</span>
<span id="cb3-10">    ui.card_header(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Message Composer"</span>)</span>
<span id="cb3-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.card_body():</span>
<span id="cb3-12">        ui.input_submit_textarea(</span>
<span id="cb3-13">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"message"</span>,</span>
<span id="cb3-14">            label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Message"</span>,</span>
<span id="cb3-15">            placeholder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Compose your message..."</span>,</span>
<span id="cb3-16">            rows<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb3-17">            toolbar<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.toolbar(</span>
<span id="cb3-18">                ui.toolbar_input_select(</span>
<span id="cb3-19">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"priority"</span>,</span>
<span id="cb3-20">                    label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Priority"</span>,</span>
<span id="cb3-21">                    choices<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Low"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Medium"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"High"</span>],</span>
<span id="cb3-22">                    selected<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Medium"</span>,</span>
<span id="cb3-23">                    icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flag"</span>),</span>
<span id="cb3-24">                ),</span>
<span id="cb3-25">                ui.toolbar_divider(),</span>
<span id="cb3-26">                ui.toolbar_input_button(</span>
<span id="cb3-27">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"attach"</span>,</span>
<span id="cb3-28">                    label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Attach"</span>,</span>
<span id="cb3-29">                    icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"paperclip"</span>),</span>
<span id="cb3-30">                ),</span>
<span id="cb3-31">                align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>,</span>
<span id="cb3-32">            ),</span>
<span id="cb3-33">        )</span>
<span id="cb3-34"></span>
<span id="cb3-35"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.card(full_screen<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"250px"</span>):</span>
<span id="cb3-36">    ui.card_header(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Sent Messages"</span>)</span>
<span id="cb3-37"></span>
<span id="cb3-38">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.card_body():</span>
<span id="cb3-39">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.ui</span></span>
<span id="cb3-40">        <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> messages_output():</span>
<span id="cb3-41">            msg_list <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> messages.get()</span>
<span id="cb3-42">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> msg_list:</span>
<span id="cb3-43">                <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"No messages sent yet."</span>, style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"color: #888;"</span>)</span>
<span id="cb3-44"></span>
<span id="cb3-45">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.div(</span>
<span id="cb3-46">                <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>[</span>
<span id="cb3-47">                    ui.p(</span>
<span id="cb3-48">                        <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"[</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>msg[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'priority'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">] </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>msg[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'text'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb3-49">                        style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"margin: 4px 0;"</span>,</span>
<span id="cb3-50">                    )</span>
<span id="cb3-51">                    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> msg <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">reversed</span>(msg_list)</span>
<span id="cb3-52">                ]</span>
<span id="cb3-53">            )</span>
<span id="cb3-54"></span>
<span id="cb3-55"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.effect</span></span>
<span id="cb3-56"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.event</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.message)</span>
<span id="cb3-57"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> _():</span>
<span id="cb3-58">    message_text <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.message()</span>
<span id="cb3-59">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> message_text <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">and</span> message_text.strip():</span>
<span id="cb3-60">        current_messages <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(messages.get())</span>
<span id="cb3-61">        current_messages.append(</span>
<span id="cb3-62">            {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text"</span>: message_text, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"priority"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.priority()}</span>
<span id="cb3-63">        )</span>
<span id="cb3-64">        messages.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">set</span>(current_messages)</span></code></pre></div>
<p>Toolbars are available in <code>py-shiny</code> and forthcoming in <a href="https://rstudio.github.io/bslib/"><code>bslib</code></a> for R. For a complete walkthrough with full app examples, see the <a href="https://shiny.posit.co/py/components/layout/toolbar/">Toolbar component page</a>.</p>
</section>
</section>
<section id="opentelemetry" class="level2">
<h2 data-anchor-id="opentelemetry">OpenTelemetry</h2>
<p>Starting with Shiny <code>v1.6.0</code>, <a href="https://opentelemetry.io/">OpenTelemetry</a> support is built directly into the framework.</p>
<p>OpenTelemetry (OTel) is a vendor-neutral observability standard that lets you collect telemetry data — traces, logs, and metrics — and send it to any compatible backend. For Shiny apps, this means you can finally answer questions like:</p>
<ul>
<li>Why is my app slow for certain users?</li>
<li>Which reactive expressions are taking the most time?</li>
<li>How long does it take for outputs to render?</li>
<li>What sequence of events occurs when a user interacts with my app?</li>
</ul>
<section id="getting-started" class="level3">
<h3 data-anchor-id="getting-started">Getting started</h3>
<p>The fastest way to get started is with <a href="https://logfire.pydantic.dev/">Pydantic Logfire</a>, which provides zero-configuration OTel setup:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install logfire</span>
<span id="cb4-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">logfire</span> auth</span></code></pre></div>
<p>Then set an environment variable to tell Shiny what level of tracing to collect:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">SHINY_OTEL_COLLECT</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>reactivity</span></code></pre></div>
<p>That’s it — no changes to your app code required. Run your app and visit <a href="https://logfire.pydantic.dev/">logfire.pydantic.dev</a> to see traces.</p>
</section>
<section id="otel-is-great-for-genai-apps" class="level3">
<h3 data-anchor-id="otel-is-great-for-genai-apps">OTel is great for GenAI apps</h3>
<p>Shiny’s OTel integration pairs especially well with Generative AI applications. When a user reports that your chatbot feels slow, traces make it easy to pinpoint whether the delay is in the AI model request, streaming, tool execution, or a downstream reactive calculation.</p>
<p>The image below shows a trace from a weather forecast app powered by a Generative AI model. A single user session is captured in full detail:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.6/weather_app_ui.png" class="img-fluid figure-img"></p>
<figcaption>The weather forecast Shiny app powered by a Generative AI model</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.6/weather_app_logfire.png" class="img-fluid figure-img"></p>
<figcaption>Trace in Logfire showing a full user session with reactive updates, model calls, and a tool invocation</figcaption>
</figure>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Collection levels
</div>
</div>
<div class="callout-body-container callout-body">
<p><code>SHINY_OTEL_COLLECT</code> accepts three levels of detail:</p>
<ul>
<li><code>"none"</code> - No Shiny OpenTelemetry tracing</li>
<li><code>"session"</code> - Track session start and end</li>
<li><code>"reactive_update"</code> - Track reactive updates (includes <code>"session"</code> tracing)</li>
<li><code>"reactivity"</code> - Trace all reactive expressions (includes <code>"reactive_update"</code> tracing)</li>
<li><code>"all"</code> [Default] - Everything (currently equivalent to “reactivity”)</li>
</ul>
</div>
</div>
</section>
<section id="what-gets-traced-automatically" class="level3">
<h3 data-anchor-id="what-gets-traced-automatically">What gets traced automatically</h3>
<p>Shiny automatically creates spans for all of the following — no manual instrumentation needed:</p>
<ul>
<li><strong>Session lifecycle</strong>: When sessions start and end, including HTTP request details</li>
<li><strong>Reactive updates</strong>: The entire cascade of reactive calculations triggered by an input change or a new output to be rendered</li>
<li><strong>Reactive expressions</strong>: Individual calculations such as <code>@reactive.calc</code>, <code>@reactive.effect</code>, <code>@render.*</code>, and other reactive constructs</li>
</ul>
</section>
<section id="works-with-any-otel-backend" class="level3">
<h3 data-anchor-id="works-with-any-otel-backend">Works with any OTel backend</h3>
<p>Logfire is our recommended starting point, but Shiny’s OTel integration is fully vendor-neutral. You can send traces to <a href="https://www.jaegertracing.io/">Jaeger</a>, <a href="https://zipkin.io/">Zipkin</a>, <a href="https://grafana.com/products/cloud/">Grafana Cloud</a>, <a href="https://langfuse.com/">Langfuse</a>, or any other OTLP-compatible backend.</p>
<p>For local debugging without a backend, install the OpenTelemetry SDK and use the console exporter:</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shiny[otel]"</span></span></code></pre></div>
<p>Full documentation — including custom spans, database instrumentation, and production considerations — is available in the <a href="https://shiny.posit.co/py/docs/opentelemetry.html">OpenTelemetry guide</a>.</p>
</section>
</section>
<section id="in-closing" class="level2">
<h2 data-anchor-id="in-closing">In closing</h2>
<p>We’re excited to bring you these new features in Shiny <code>v1.6</code>. As always, if you have questions or feedback, <a href="https://discord.gg/yMGCamUMnS">join us on Discord</a> or <a href="https://github.com/posit-dev/py-shiny/issues/new">open an issue on GitHub</a>. Happy Shiny-ing!</p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-python-1.6/</guid>
  <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-python-1.6/py-shiny-otel-toolbars.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Where Questions Become Queries: Meet querychat</title>
  <dc:creator>Veerle Eeftink - van Leemput</dc:creator>
  <link>https://shiny.posit.co/blog/posts/querychat-python-r/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/querychat-python-r/querychat-python-r-header.png" alt="Where Questions Become Queries: Meet querychat" style="width: 100%; height: auto;">
    </div>
    


<p>You love data. And you love building dashboards with it, especially with your favourite tool Shiny. But even with a bullet-proof design, dozens of user stories, feedback loops, and adjustments, you also know that there are always questions that your dashboard leaves unanswered.</p>
<p>Let’s say you developed a dashboard to display women’s international soccer matches. You are proud of what you have built and you eagerly show it to a colleague:</p>
<p><strong>Colleague</strong>:&nbsp;“Amazing! Can you show me the soccer matches for the FIFA World Cup only?”</p>
<p><strong>You</strong>:&nbsp;“Of course, let me filter it down for you and select the FIFA World Cup tournaments”</p>
<p><strong>Colleague</strong>:&nbsp;“Interesting, can you show me all the matches in which The Netherlands have played?”</p>
<p><strong>You</strong>:&nbsp;“Eh… Well, I could, but I just have to include a country filter in my dashboard then!”</p>
<p>Right. How are we supposed to filter down to a specific country if there is no input for it? And what about getting summary statistics for countries or players? By now it becomes painfully clear that our soccer dashboard has its limits.</p>
<p>This is exactly the moment when&nbsp;<a href="https://posit-dev.github.io/querychat"><code>querychat</code></a>&nbsp;becomes interesting. It is a multilingual package that allows you to chat with your data using natural language queries. No more clicking, no more limited filters, just you and your questions. And in this article, you’re going to learn everything about it!</p>
<p>To bring <code>querychat</code> to life, we will keep returning to two examples:</p>
<ul>
<li>The classic diamonds dataset. After all, diamonds are a girl’s best friend, and a data scientist’s too! The familiar dataset offers a mix of variables such as cut, colour, clarity and price, which makes it ideal for all sorts of natural language questions. You might wonder about average prices for particular cuts, or you want to compare colours, look at how clarity affects value, or explore simple patterns in the data. In other words, it is a perfect playground for testing how well natural language queries behave on structured data.</li>
<li>SheScores, the soccer dashboard that you were so proud of earlier. This app originates from the shiny::conf(2024) workshop&nbsp;<a href="https://github.com/hypebright/shinyconf2024-shiny101">“Shiny 101: The Modular App Blueprint”</a>, although it has been tweaked to make it a bit more interesting and updated with matches through to November 2025.</li>
</ul>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/shescores-original-py.png" class="img-fluid"></p>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/shescores-original-r.png" class="img-fluid"></p>
</div>
</div>
</div>
<p>Both datasets set the stage nicely, so let’s roll the ball and see how&nbsp;<code>querychat</code>&nbsp;plays. We’re talking about soccer after all!</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Full code available on GitHub
</div>
</div>
<div class="callout-body-container callout-body">
<p>Instead of copy-pasting the content of this blog into your favourite IDE, you can also <a href="https://github.com/hypebright/shescores-dashboard">pull the project from GitHub</a> and follow along. All the code is available in both Python and R.</p>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Short on time?
</div>
</div>
<div class="callout-body-container callout-body">
<p>Jump straight to the SheScores app with querychat or visit the <a href="https://posit-dev.github.io/querychat">querychat</a> website</p>
</div>
</div>
<section id="hello-querychat" class="level1">
<h1>Hello, querychat</h1>
<p>In short, <code>querychat</code> makes it easy to query data using natural language. It offers a drop-in component for Shiny, a console interface, and other programmatic building-blocks. You ask questions, <code>querychat</code> translates it to a SQL query, executes it, and returns the results. The results are available as a reactive data frame, which makes it easy to display or further process the data.</p>
<p><code>querychat</code> would solve the problem we encountered earlier. We can ask it any question we can imagine without constantly adding filters or other analysis. No country filter? No problem. And yes, that sounds as cool as it is!</p>
<p>So, what do we need?</p>
<p><code>querychat</code> is powered by a Large Language Model (LLM), so you need access to a model. You first need to register at an LLM provider that provides those models. You can choose any model you like, with two little “restrictions”: <a href="https://posit-dev.github.io/chatlas/"><code>chatlas</code></a> (Python) or <a href="https://ellmer.tidyverse.org"><code>ellmer</code></a> (R) supports it (which shouldn’t be hard, because all the major models are) and the model has the ability to do tool calls.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Recommended models
</div>
</div>
<div class="callout-body-container callout-body">
<p>In this blog we’ll use Claude Sonnet 4.5 from Anthropic. Other good choices would be GPT-4.1 (the current default for <code>querychat</code>) and Google Gemini 3.0 (as of November 2025).</p>
</div>
</div>
<p>Once you’ve made your choice and registered, you can get an API key. You need this key to authenticate with the LLM provider. One important note: never, ever hardcode the key directly into your script. You’ll be amazed how many keys are publicly available on GitHub repos. Don’t be that developer. As always with secrets, store it as an environment variable. Just note that the exact name of the key depends on the provider. For example, Anthropic expects <code>ANTHROPIC_API_KEY=yourkey</code>, while OpenAI uses&nbsp;<code>OPENAI_API_KEY=yourkey</code>.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-2-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-1" aria-controls="tabset-2-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-2-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-2" aria-controls="tabset-2-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-2-1" class="tab-pane active" aria-labelledby="tabset-2-1-tab">
<p>In Python, the recommended approach is to create a&nbsp;<code>.env</code>&nbsp;file in your project folder:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1">ANTHROPIC_API_KEY<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>yourkey</span></code></pre></div>
<p>It’s recommended to use the&nbsp;<code>dotenv</code>&nbsp;package to load the&nbsp;<code>.env</code>&nbsp;file into your environment:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb2-2">load_dotenv()</span></code></pre></div>
<p>To keep the demo code concise we’ll omit these lines from subsequent code examples.</p>
</div>
<div id="tabset-2-2" class="tab-pane" aria-labelledby="tabset-2-2-tab">
<p>In R, environment variables are typically stored in a&nbsp;<code>.Renviron</code>&nbsp;file. You can create this file in your project root or in your home directory (<code>~/.Renviron</code>). Or, if you want to make it yourself really easy: you can also open/edit the relevant file with&nbsp;<code>usethis::edit_r_environ()</code>.</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">ANTHROPIC_API_KEY<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span>yourkey</span></code></pre></div>
</div>
</div>
</div>
<p>Of course we can’t use <code>querychat</code> without installing it, so that’s the next step:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-3-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-1" aria-controls="tabset-3-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-3-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-2" aria-controls="tabset-3-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-3-1" class="tab-pane active" aria-labelledby="tabset-3-1-tab">
<p>For Python,&nbsp;<code>querychat</code>&nbsp;is available on PyPI, so you can install it easily with&nbsp;<code>pip</code>:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install querychat</span></code></pre></div>
<p>Or, if you’re using&nbsp;<code>uv</code>, add it like so:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">uv</span> add querychat</span></code></pre></div>
<p>Once installed, import it like this:</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> querychat</span></code></pre></div>
</div>
<div id="tabset-3-2" class="tab-pane" aria-labelledby="tabset-3-2-tab">
<p>You can get <code>querychat</code> from CRAN using:</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"querychat"</span>)</span></code></pre></div>
<p>Alternatively, if you want the latest development version, you can install&nbsp;<code>querychat</code>&nbsp;from GitHub using:</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1">pak<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pak</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit-dev/querychat/pkg-r"</span>)</span></code></pre></div>
<p>Once installed, load the package as usual:</p>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(querychat)</span></code></pre></div>
</div>
</div>
</div>
<p>Wouldn’t it be great if you can use <code>querychat</code> straight away without much code? Just to see what it’s all about? Luckily you can with the “quick launch” Shiny app! You can simply call <code>app()</code> which spins up an app with <code>querychat</code> chat interface. Let’s try it out for our diamonds dataset:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-4-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-1" aria-controls="tabset-4-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-4-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-2" aria-controls="tabset-4-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-4-1" class="tab-pane active" aria-labelledby="tabset-4-1-tab">
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> seaborn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dataset</span>
<span id="cb10-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> querychat <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> QueryChat</span>
<span id="cb10-3"></span>
<span id="cb10-4">diamonds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> load_dataset(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>)</span>
<span id="cb10-5">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(diamonds, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>, client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>)</span>
<span id="cb10-6">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> qc.app()</span></code></pre></div>
<p><code>qc = QueryChat(...)</code>&nbsp;creates an instance of the&nbsp;<code>QueryChat</code>&nbsp;class. You pass in the dataset, give it a name and specify the model client (powered by <a href="https://posit-dev.github.io/chatlas/"><code>chatlas</code></a>).&nbsp;<code>qc.app()</code>&nbsp;returns the web app that lets you explore the diamonds data using natural language questions.</p>
<p>Want to try this with a different provider and/or model? No problem, just change the <code>client</code> argument accordingly. For example, to use GPT-4.1 from OpenAI, you would write: <code>client="openai/gpt-4.1"</code>. You can learn more about the different options in the <a href="https://posit-dev.github.io/querychat/py/models.html"><code>querychat</code> documentation</a>.</p>
<p>To run this app, you need to save the code above in a file (and call it <a href="http://diamonds-app.py"><code>diamonds-app.py</code></a> for example) and run it like so:</p>
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1">shiny run <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">reload</span> diamonds<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>app.py </span></code></pre></div>
</div>
<div id="tabset-4-2" class="tab-pane" aria-labelledby="tabset-4-2-tab">
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb12-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb12-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb12-3"></span>
<span id="cb12-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querychat_app</span>(diamonds, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span>)</span></code></pre></div>
<p>Or, alternatively, you could write:</p>
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb13-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb13-3"></span>
<span id="cb13-4">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb13-5">  diamonds,</span>
<span id="cb13-6">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb13-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span></span>
<span id="cb13-8">)</span>
<span id="cb13-9"></span>
<span id="cb13-10">qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">app</span>()</span></code></pre></div>
<p>Both result in the same outcome, the first one is just a simplified version.</p>
<p><code>QueryChat$new()</code>&nbsp;creates the R6 object, taking the dataset, a table name and the model client (which will be passed to&nbsp;<code>ellmer::chat()</code>). Calling&nbsp;<code>qc$app()</code>&nbsp;then launches the Shiny app so you can query the diamonds dataset in plain English.</p>
<p>Want to change the provider and/or model? No problem, just change the <code>client</code> argument accordingly. For example, to use GPT-4.1 from OpenAI, you would write: <code>client = "openai/gpt-4.1"</code>. You can learn more about the different options in the <a href="https://posit-dev.github.io/querychat/r/index.html#use-a-different-llm-provider"><code>querychat</code> documentation</a>.</p>
</div>
</div>
</div>
<p>The result: a Shiny app that allows users to interact with a data source using natural language queries.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-5-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-1" aria-controls="tabset-5-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-5-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-2" aria-controls="tabset-5-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-5-1" class="tab-pane active" aria-labelledby="tabset-5-1-tab">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/diamonds-py.png" class="img-fluid"></p>
</div>
<div id="tabset-5-2" class="tab-pane" aria-labelledby="tabset-5-2-tab">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/diamonds-r.png" class="img-fluid"></p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Custom branding
</div>
</div>
<div class="callout-body-container callout-body">
<p>Do you notice the nice green touches and custom font in this demo app? That’s because the project we’ll be using in this article uses <a href="https://posit-dev.github.io/brand-yml/">brand.yml</a>: a simple, portable YAML file that codifies brand guidelines into a format that can be used by Quarto, Python and R. And in this case, it works beautifully for Shiny. Curious to see what such a <code>_brand.yml</code> file looks like? You can check it out <a href="https://github.com/hypebright/shescores-dashboard/blob/0cd4e3f3ae52bcf4a39f7d63fb26e555de9a6b5e/_brand.yml">here</a>.</p>
</div>
</div>
</div>
</div>
</div>
<p>You can ask the diamonds dataset some surprisingly rich questions, and&nbsp;<code>querychat</code>&nbsp;handles them with ease. A simple place to begin is something like “show the 10 most expensive diamonds”. It produces straightforward SQL and updates the table in the app instantly.</p>
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode sql code-with-copy"><code class="sourceCode sql"><span id="cb14-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">SELECT</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> diamonds <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">ORDER</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">BY</span> price <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">DESC</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">LIMIT</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span></span></code></pre></div>
<p>The question may be simple, but it already highlights the convenience of&nbsp;<code>querychat</code>. Without it, users would need to sort the table manually or rely on a picker, slider or some other input that filters the data for this very specific request.</p>
<p>Things get more interesting when we introduce a calculation. Asking “can you show the 20 biggest diamonds, based on volume?” still results in simple SQL, but the output now includes an extra column, volume, which appears neatly in the app.</p>
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode sql code-with-copy"><code class="sourceCode sql"><span id="cb15-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Calculate volume (x * y * z) and sort by largest volume</span></span>
<span id="cb15-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">SELECT</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>, </span>
<span id="cb15-3">       x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> z <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> volume</span>
<span id="cb15-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> diamonds </span>
<span id="cb15-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">ORDER</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">BY</span> volume <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">DESC</span> </span>
<span id="cb15-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">LIMIT</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span></span></code></pre></div>
<p>From there, we can try some grouping and window functions. “Within each cut, what is the most expensive diamond?” works perfectly, showing the grouped results along with the necessary window function behind the scenes.</p>
<div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode sql code-with-copy"><code class="sourceCode sql"><span id="cb16-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Find the most expensive diamond for each cut type</span></span>
<span id="cb16-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">SELECT</span> </span>
<span id="cb16-3">    cut,</span>
<span id="cb16-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">MAX</span>(price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> max_price,</span>
<span id="cb16-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Get the details of the diamond with the max price for each cut</span></span>
<span id="cb16-6">    ARG_MAX(carat, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> carat,</span>
<span id="cb16-7">    ARG_MAX(color, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> color,</span>
<span id="cb16-8">    ARG_MAX(clarity, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> clarity,</span>
<span id="cb16-9">    ARG_MAX(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">depth</span>, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">depth</span>,</span>
<span id="cb16-10">    ARG_MAX(<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">"table"</span>, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> table_pct</span>
<span id="cb16-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> diamonds</span>
<span id="cb16-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">GROUP</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">BY</span> cut</span>
<span id="cb16-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">ORDER</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">BY</span> max_price <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">DESC</span></span></code></pre></div>
<div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode txt code-with-copy"><code class="sourceCode default"><span id="cb17-1">        cut max_price carat color clarity depth table_pct</span>
<span id="cb17-2">1   Premium     18823  2.29     I     VS2  60.8        60</span>
<span id="cb17-3">2 Very Good     18818  2.00     G     SI1  63.5        56</span>
<span id="cb17-4">3     Ideal     18806  1.51     G      IF  61.7        55</span>
<span id="cb17-5">4      Good     18788  2.80     G     SI2  63.8        58</span>
<span id="cb17-6">5      Fair     18574  2.01     G     SI1  70.6        64</span></code></pre></div>
<p>Now, for a grand finale, we can throw in a more analytical question: “are larger physical dimensions always associated with higher price?” This one generates a slightly more complex SQL query, but it also comes with a clear and helpful explanation.</p>
<div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode sql code-with-copy"><code class="sourceCode sql"><span id="cb18-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Calculate correlation between physical dimensions and price</span></span>
<span id="cb18-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Also look for outliers where small diamonds cost more than large ones</span></span>
<span id="cb18-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">WITH</span> dimension_stats <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> (</span>
<span id="cb18-4">  <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">SELECT</span> </span>
<span id="cb18-5">    carat,</span>
<span id="cb18-6">    x, y, z,</span>
<span id="cb18-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Calculate volume as a proxy for overall size</span></span>
<span id="cb18-8">    x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> z <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> volume,</span>
<span id="cb18-9">    price,</span>
<span id="cb18-10">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Rank by volume and price separately</span></span>
<span id="cb18-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">RANK</span>() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">OVER</span> (<span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">ORDER</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">BY</span> x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> z <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">DESC</span>) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> volume_rank,</span>
<span id="cb18-12">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">RANK</span>() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">OVER</span> (<span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">ORDER</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">BY</span> price <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">DESC</span>) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> price_rank</span>
<span id="cb18-13">  <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> diamonds</span>
<span id="cb18-14">  <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">WHERE</span> x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AND</span> y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AND</span> z <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Exclude malformed records</span></span>
<span id="cb18-15">)</span>
<span id="cb18-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">SELECT</span> </span>
<span id="cb18-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CORR</span>(x, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> x_price_correlation,</span>
<span id="cb18-18">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CORR</span>(y, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> y_price_correlation,</span>
<span id="cb18-19">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CORR</span>(z, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> z_price_correlation,</span>
<span id="cb18-20">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">CORR</span>(volume, price) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> volume_price_correlation,</span>
<span id="cb18-21">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Count cases where rank differences are significant (outliers)</span></span>
<span id="cb18-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">COUNT</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">CASE</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">WHEN</span> volume_rank <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> price_rank <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">THEN</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">END</span>) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> large_cheap_diamonds,</span>
<span id="cb18-23">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">COUNT</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">CASE</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">WHEN</span> price_rank <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> volume_rank <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">THEN</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">END</span>) <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">AS</span> small_expensive_diamonds</span>
<span id="cb18-24"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">FROM</span> dimension_stats</span></code></pre></div>
<div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode txt code-with-copy"><code class="sourceCode default"><span id="cb19-1">x_price_correlation        : 0.8872314</span>
<span id="cb19-2">y_price_correlation        : 0.8678642</span>
<span id="cb19-3">z_price_correlation        : 0.8682064</span>
<span id="cb19-4">volume_price_correlation   : 0.9042547</span>
<span id="cb19-5">large_cheap_diamonds       : 21082</span>
<span id="cb19-6">small_expensive_diamonds   : 21065</span></code></pre></div>
<p>The conclusion?</p>
<div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode txt code-with-copy"><code class="sourceCode default"><span id="cb20-1">Answer: No, larger physical dimensions are NOT always associated with higher price</span>
<span id="cb20-2"></span>
<span id="cb20-3">While there's a strong positive correlation (~0.90 for overall volume), quality factors can override size.</span>
<span id="cb20-4"></span>
<span id="cb20-5">Correlation Analysis</span>
<span id="cb20-6">Volume ↔ Price: 0.90 correlation</span>
<span id="cb20-7">X dimension ↔ Price: 0.89 correlation</span>
<span id="cb20-8">Y dimension ↔ Price: 0.87 correlation</span>
<span id="cb20-9">Z dimension ↔ Price: 0.87 correlation</span>
<span id="cb20-10">This means physical size explains about 80-90% of price variation, but not all of it.</span>
<span id="cb20-11"></span>
<span id="cb20-12">The Key Finding</span>
<span id="cb20-13">I found over 21,000 cases where small diamonds are more expensive than much larger ones, and vice versa. This happens because quality factors (cut, color, clarity) matter significantly.</span>
<span id="cb20-14"></span>
<span id="cb20-15">Concrete Example</span>
<span id="cb20-16">Here's a smaller diamond that costs more per unit volume than typical large diamonds:</span>
<span id="cb20-17"></span>
<span id="cb20-18">Small, premium diamond: 74mm³ volume, 1.41 carat, $9,752 (Ideal cut, H color, VS1 clarity)</span>
<span id="cb20-19">This costs $131 per mm³ of volume</span>
<span id="cb20-20">Compare this to larger diamonds with lower quality that sell for much less per unit volume due to poor cut, color, or clarity grades.</span></code></pre></div>
<p>To encourage further exploration, <code>querychat</code> presents suggestions such as comparing how cut quality affects the price to carat ratio, finding diamonds where clarity has the biggest impact on price, or checking the price difference between the best and worst colour grades for similar sized stones. Yeah, that’s right, you don’t even have to come up with questions yourself.</p>
<p>Some questions result in a filtered table, others result in an explanation with results in the chat window. <code>querychat</code> figures out, based on your question, whether you want an answer straight away, or want to inspect the filtered data yourself. Pretty cool that this only took a few lines of code.</p>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
LLMs can make mistakes
</div>
</div>
<div class="callout-body-container callout-body">
<p>Note that it’s still an LLM that generates these queries. LLMs can make mistakes. The nice thing about <code>querychat</code> though, is that you can inspect the SQL query yourself.</p>
<p>Most issues fall into two categories:</p>
<ul>
<li>Query errors: the SQL may fail to run or may not fully reflect what you intended. When it fails, the model will often try again. In this case, giving more context about the data can help.</li>
<li>Result errors: even when the query is correct, the model may misunderstand or oversimplify the results, especially if that result is large or complex. The result might be that key insights are missed or misinterpreted.</li>
</ul>
</div>
</div>
</section>
<section id="why-this-matters-reliability-transparency-reproducibility" class="level1">
<h1>Why this matters: reliability, transparency, reproducibility</h1>
<p>What makes the “quick launch” app so powerful is that it is far more than a chat window sitting on top of a dataset. Think back to the questions we explored earlier. We filtered, sorted, computed new columns, grouped data and used window functions. We also looked at analytical relationships without writing a single line of code. And that is only the beginning. If you want to go further, you can hunt for anomalies, create categories, build benchmarks or explore almost any analysis you can imagine. The key is that you never have to think about&nbsp;<em>how</em>&nbsp;to do it. You just ask.</p>
<p>And yes, you could ask all those questions in a typical LLM chat tool. But <code>querychat</code> is different. You are not relying on the model to&nbsp;<em>invent</em>&nbsp;answers or reason about the data internally. Instead, every single question is translated into SQL, executed on the actual dataset and returned exactly as the data dictates. And crucially, the SQL is always shown, so you can see precisely what is being run.</p>
<p>This brings four important benefits:</p>
<ul>
<li><strong>Reliability:</strong>&nbsp;the LLM does not analyse or transform the raw data itself. It only generates SQL text.&nbsp;<code>querychat</code>&nbsp;handles the execution of that SQL via tool calling so all results come from the real data engine, not from the model’s internal guesswork.</li>
<li><strong>Transparency:</strong>&nbsp;every query reveals the full SQL statement. Nothing is hidden, nothing is adjusted, and you always know how the answer was produced.</li>
<li><strong>Reproducibility:</strong>&nbsp;since every SQL query is visible, analyses can be reused, shared, and audited.</li>
<li><strong>Safety</strong>: <code>querychat</code>’s tools are designed with read-only actions in mind, meaning the LLM is essentially unable to perform destructive actions. However, to fully guarantee no destructive actions on your production database, make sure <code>querychat</code>’s database permissions are read-only!</li>
</ul>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-6-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-1" aria-controls="tabset-6-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-6-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-2" aria-controls="tabset-6-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-6-1" class="tab-pane active" aria-labelledby="tabset-6-1-tab">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/diamonds-drop-py.png" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:50.0%"></p>
</figure>
</div>
</div>
<div id="tabset-6-2" class="tab-pane" aria-labelledby="tabset-6-2-tab">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/diamonds-drop-r.png" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:50.0%"></p>
</figure>
</div>
</div>
</div>
</div>
</section>
<section id="how-it-works-tool-calling" class="level1">
<h1>How it works: tool calling</h1>
<p>If you read&nbsp;<a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/#when-llms-guess-tools-know">The Shiny Side of LLMs</a>&nbsp;blog series, you already know a bit about tool calling. In that series we explored how LLMs can call external tools instead of trying to do everything themselves, and&nbsp;<code>querychat</code>&nbsp;is a very practical example of this idea in action.</p>
<p>Tool calling is essentially a bridge between an LLM and your Python or R session. The model does not execute code. Instead, it requests your Python or R session execute a certain function with certain inputs (e.g., a SQL statement). Once Python or R performs the execution, the result is then passed back to the model for interpretation.</p>
<p>So how does tool calling help us here? Well, LLMs have their strengths and weaknesses. They are not great at counting things, creating data summaries or doing basic calculations. But they&nbsp;<em>are</em>&nbsp;excellent at taking natural language and turning it into structured code. SQL that is. This SQL is then executed through a tool call: a function that executes the (read only) SQL. In both Python and R this means the LLM can express your question as a request to call a function with precise arguments, and the host language performs the real work. This makes it all reliable, reproducible, and safe (read only SQL).</p>
<p>Given that, generally speaking, LLMs are very good at writing SQL, it makes perfect sense to ask one to translate your natural language questions into SQL queries. In order to generate SQL that can be executed, the LLM does need to know something about your data: which columns are there, what do they mean, and what type are they? This <strong>schema information</strong> is shared with the model, but not the raw data. With this information, it produces an SQL query as a tool call. Now, to run SQL you need a database engine. <code>querychat</code>’s weapon of choice: <a href="https://duckdb.org">DuckDB</a>. Basically, our diamonds dataset gets turned into a DuckDB database, and generated SQL queries are executed on this database. Then the results are passed back to the LLM so it can say some interesting things about it.</p>
<p>To summarise:</p>
<p>prompt → SQL query → tool call → execute SQL query → return results<sup>1</sup></p>
<p>Tool calling is worth emphasising because it gives us a controlled and predictable interface between LLMs and real code execution. Instead of writing and maintaining your own custom tools, you can turn to&nbsp;<code>querychat</code>. It already provides the functions needed to turn natural language into reliable SQL that Python or R can execute with confidence.</p>
</section>
<section id="customising-querychat-from-chat-to-toolkit" class="level1">
<h1>Customising <code>querychat</code>: from chat to toolkit</h1>
<p>Alright, enough talking. You now know what <code>querychat</code> can do, and how it does it (high-level). You might even have brilliant ideas for your next app… In that case it would be nice to know how to build your own app with <code>querychat</code>. The Diamonds “quick launch” app from earlier, that you run with <code>qc.app()</code> (Python) or <code>qc$app()</code> (R), consists of a handful of methods that you can find in <code>querychat</code>, and we’re going to use them directly.</p>
<p>The main component is the <code>QueryChat</code> object, which has different arguments and methods.</p>
<section id="querychat-object" class="level2">
<h2 class="anchored" data-anchor-id="querychat-object">QueryChat object</h2>
<p>You call <code>QueryChat</code> to initialise a <code>QueryChat</code> object (often called <code>qc</code>), like so:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-7-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-1" aria-controls="tabset-7-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-7-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-2" aria-controls="tabset-7-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-7-1" class="tab-pane active" aria-labelledby="tabset-7-1-tab">
<div class="sourceCode" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(...)</span></code></pre></div>
</div>
<div id="tabset-7-2" class="tab-pane" aria-labelledby="tabset-7-2-tab">
<div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb22-1">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(...)</span></code></pre></div>
</div>
</div>
</div>
<p>You can pass <code>QueryChat</code> several arguments:</p>
<ul>
<li><p><code>data_source</code> and <code>table_name</code></p>
<p>These are the two most important arguments: they specify your data source and the name of your table that can be used for the SQL queries. The data source can be your data frame, a tibble, a table or any other Python or R data object, and the table name is usually the variable name of your data frame. In our example our <code>data_source</code> was <code>diamonds</code>, which we also stored in a variable called <code>diamonds</code>.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-8-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-1" aria-controls="tabset-8-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-8-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-2" aria-controls="tabset-8-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-8-1" class="tab-pane active" aria-labelledby="tabset-8-1-tab">
<div class="sourceCode" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(diamonds, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>)</span></code></pre></div>
</div>
<div id="tabset-8-2" class="tab-pane" aria-labelledby="tabset-8-2-tab">
<p>Generally, in R, the table name isn’t required as it can be inferred from the variable name. However, it is required when you use a database connection, which we’ll use later.</p>
<div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb24-1">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb24-2">  diamonds,</span>
<span id="cb24-3">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span></span>
<span id="cb24-4">)</span></code></pre></div>
</div>
</div>
</div>
<p>You’re not limited to data objects: you can also pass a database connection to <code>data_source</code>. We’ll come back to that later.</p></li>
<li><p><code>client</code></p>
<p>We used the <code>client</code> argument before: we use it to tell <code>querychat</code> that we want to use Claude Sonnet 4.5 (or any other model). This gets us back at the starting point of our Diamonds “quick launch” app.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-9-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-9-1" aria-controls="tabset-9-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-9-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-9-2" aria-controls="tabset-9-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-9-1" class="tab-pane active" aria-labelledby="tabset-9-1-tab">
<div class="sourceCode" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(diamonds, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>, client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>)</span></code></pre></div>
<p>Alternatively, you can set the client in options the <code>QUERYCHAT_CLIENT</code> environment variable.</p>
</div>
<div id="tabset-9-2" class="tab-pane" aria-labelledby="tabset-9-2-tab">
<div class="sourceCode" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb26-1">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb26-2">  diamonds,</span>
<span id="cb26-3">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb26-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span></span>
<span id="cb26-5">)</span></code></pre></div>
<p>Alternatively, you can set the client in options with <code>options(querychat.client = "claude/claude-sonnet-4-5")</code>.</p>
</div>
</div>
</div></li>
<li><p><code>id</code></p>
<p>This is an optional argument, and if it’s not given it’s derived from the <code>table_name</code>. When to use it? If you want to work with <a href="https://posit-dev.github.io/querychat/py/build.html#multiple-datasets">multiple QueryChat instances</a>, for example.</p></li>
<li><p><code>greeting</code></p>
<p>A nice greeting message to display to your users. It’s the first thing your users see, so you better make it good! If not provided, one is generated at the start. While this one looks fine on first sight, it’s rather slow and wasteful (it costs extra tokens because it’s generated every single time). Also, because it’s generated on the fly, it’s far from consistent. Earlier, when we ran the “quick launch” app, you already might have noticed that it generated a warning message:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-10-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-10-1" aria-controls="tabset-10-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-10-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-10-2" aria-controls="tabset-10-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-10-1" class="tab-pane active" aria-labelledby="tabset-10-1-tab">
<div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode txt code-with-copy"><code class="sourceCode default"><span id="cb27-1">Warning: No greeting provided; the LLM will be invoked at conversation start to generate one. For faster startup, lower cost, and determinism, please save a greeting and pass it to init(). You can also use `querychat.greeting()` to help generate a greeting.</span></code></pre></div>
</div>
<div id="tabset-10-2" class="tab-pane" aria-labelledby="tabset-10-2-tab">
<div class="sourceCode" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode txt code-with-copy"><code class="sourceCode default"><span id="cb28-1">Warning message:</span>
<span id="cb28-2">No greeting provided; the LLM will be invoked at conversation start to generate one.</span>
<span id="cb28-3">• For faster startup, lower cost, and determinism, please save a greeting and pass it to QueryChat$new().</span>
<span id="cb28-4">ℹ You can generate a greeting with $generate_greeting(). </span></code></pre></div>
</div>
</div>
</div>
<p>So yes, we need a greeting! You can add your own greeting by providing a string in Markdown format.</p>
<p>Some inspiration on what you can put in there: basic instructions, suggestions for filtering, sorting or analysing the data, addressing data privacy concerns, or letting people know where they can get support if something goes wrong.</p>
<p>And if you don’t feel like writing your own greeting, or if you feel uninspired, you can let <code>querychat</code> handle it! Simply use <code>generate_greeting()</code>:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-11-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-11-1" aria-controls="tabset-11-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-11-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-11-2" aria-controls="tabset-11-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-11-1" class="tab-pane active" aria-labelledby="tabset-11-1-tab">
<div class="sourceCode" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb29-1">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(diamonds, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>, client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>)</span>
<span id="cb29-2"></span>
<span id="cb29-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate a greeting with help from the LLM</span></span>
<span id="cb29-4">greeting_text <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> qc.generate_greeting()</span>
<span id="cb29-5"></span>
<span id="cb29-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Save it</span></span>
<span id="cb29-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"w"</span>) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> f:</span>
<span id="cb29-8">    f.write(greeting_text)</span>
<span id="cb29-9"></span>
<span id="cb29-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Then use the saved greeting in your app</span></span>
<span id="cb29-11">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(diamonds, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>, client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>, greeting<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>))</span></code></pre></div>
<p>Which give us this nice greeting:</p>
<div class="sourceCode" id="cb30" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb30-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Welcome! 👋</span></span>
<span id="cb30-2"></span>
<span id="cb30-3">I'm here to help you explore and understand your diamonds dataset. </span>
<span id="cb30-4">I can filter and sort the data, answer questions with SQL queries, </span>
<span id="cb30-5">and help you discover insights.</span>
<span id="cb30-6"></span>
<span id="cb30-7">Here are some ideas to get started:</span>
<span id="cb30-8"></span>
<span id="cb30-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Explore the data</span></span>
<span id="cb30-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">* </span>&lt;span class="suggestion"&gt;Show me the most expensive diamonds&lt;/span&gt;</span>
<span id="cb30-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">* </span>&lt;span class="suggestion"&gt;What's the average price of diamonds by cut quality?&lt;/span&gt;</span>
<span id="cb30-12"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">* </span>&lt;span class="suggestion"&gt;How many diamonds are in each clarity category?&lt;/span&gt;</span>
<span id="cb30-13"></span>
<span id="cb30-14"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Filter and analyze</span></span>
<span id="cb30-15"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">* </span>&lt;span class="suggestion"&gt;Show only ideal cut diamonds over 2 carats&lt;/span&gt;</span>
<span id="cb30-16"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">* </span>&lt;span class="suggestion"&gt;Filter to diamonds with the best color grades (D, E, F)&lt;/span&gt;</span>
<span id="cb30-17"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">* </span>&lt;span class="suggestion"&gt;Which cut has the highest average price per carat?&lt;/span&gt;</span>
<span id="cb30-18"></span>
<span id="cb30-19">What would you like to explore first?</span></code></pre></div>
</div>
<div id="tabset-11-2" class="tab-pane" aria-labelledby="tabset-11-2-tab">
<div class="sourceCode" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb31-1">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb31-2">  diamonds,</span>
<span id="cb31-3">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb31-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span></span>
<span id="cb31-5">)</span>
<span id="cb31-6"></span>
<span id="cb31-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate a greeting with help from the LLM</span></span>
<span id="cb31-8">greeting_text <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">generate_greeting</span>()</span>
<span id="cb31-9"></span>
<span id="cb31-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Save it</span></span>
<span id="cb31-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">writeLines</span>(greeting_text, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>)</span>
<span id="cb31-12"></span>
<span id="cb31-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Then use the saved greeting in your app</span></span>
<span id="cb31-14">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb31-15">  diamonds,</span>
<span id="cb31-16">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb31-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span>,</span>
<span id="cb31-18">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">greeting =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span></span>
<span id="cb31-19">)</span></code></pre></div>
<p>Which give us this nice greeting:</p>
<div class="sourceCode" id="cb32" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb32-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Welcome to the Diamond Dashboard! 💎</span></span>
<span id="cb32-2"></span>
<span id="cb32-3">I'm here to help you explore and analyze this dataset of diamond characteristics and prices. </span>
<span id="cb32-4">I can filter and sort the data, answer questions, and help you discover interesting patterns.</span>
<span id="cb32-5"></span>
<span id="cb32-6">Here are some ideas to get started:</span>
<span id="cb32-7"></span>
<span id="cb32-8">**Explore the Data**</span>
<span id="cb32-9"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;What's the average price of diamonds in this dataset?&lt;/span&gt;</span>
<span id="cb32-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;How many diamonds are there in each clarity category?&lt;/span&gt;</span>
<span id="cb32-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Which diamond has the highest price?&lt;/span&gt;</span>
<span id="cb32-12"></span>
<span id="cb32-13">**Filter and Sort**</span>
<span id="cb32-14"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Show me only Ideal cut diamonds&lt;/span&gt;</span>
<span id="cb32-15"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Filter to diamonds over 2 carats and sort by price&lt;/span&gt;</span>
<span id="cb32-16"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Show me the most expensive diamonds with VS1 clarity&lt;/span&gt;</span>
<span id="cb32-17"></span>
<span id="cb32-18">What would you like to explore?</span></code></pre></div>
</div>
</div>
</div>
<p>You can see that the generated greeting contains a span HTML tag: <code>&lt;span class="suggestion"&gt;…&lt;/span&gt;</code>. If you make your own greeting, you can use this tag to automatically populate the chatbox when it’s being clicked.</p></li>
<li><p><code>data_description</code></p>
<p><code>querychat</code> automatically helps the LLM by providing things like the column names and datatypes of your data (the <strong>schema information</strong>), but results can be even more accurate when you provide additional context in the data description. There’s no specific format needed, and you can add whatever information you like. To give some inspiration, this is what we could say about the diamonds dataset:</p>
<div class="sourceCode" id="cb33" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb33-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Diamonds Dataset Description</span></span>
<span id="cb33-2"></span>
<span id="cb33-3">A structured dataset describing physical and quality attributes of individual diamonds,</span>
<span id="cb33-4">commonly used to model or predict price.</span>
<span id="cb33-5"></span>
<span id="cb33-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Fields</span></span>
<span id="cb33-7"></span>
<span id="cb33-8"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>carat (float) — Diamond weight  </span>
<span id="cb33-9"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>cut (category) — Cut quality: Fair, Good, Very Good, Premium, Ideal  </span>
<span id="cb33-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>color (category) — Color grade from D (best) to J (worst)  </span>
<span id="cb33-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>clarity (category) — Clarity grades: I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF  </span>
<span id="cb33-12"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>depth (float) — Total depth percentage  </span>
<span id="cb33-13"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>table (float) — Table width percentage  </span>
<span id="cb33-14"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>price (int) — Price in USD  </span>
<span id="cb33-15"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>x (float) — Length in mm  </span>
<span id="cb33-16"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>y (float) — Width in mm  </span>
<span id="cb33-17"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>z (float) — Depth in mm</span></code></pre></div>
<p>We can save this in a Markdown file and pass it on to <code>querychat</code>:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-12-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-12-1" aria-controls="tabset-12-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-12-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-12-2" aria-controls="tabset-12-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-12-1" class="tab-pane active" aria-labelledby="tabset-12-1-tab">
<div class="sourceCode" id="cb34" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb34-1">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(</span>
<span id="cb34-2">    diamonds,</span>
<span id="cb34-3">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb34-4">    client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>,</span>
<span id="cb34-5">    greeting<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>),</span>
<span id="cb34-6">    data_description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_data_description.md"</span>)</span>
<span id="cb34-7">)</span></code></pre></div>
</div>
<div id="tabset-12-2" class="tab-pane" aria-labelledby="tabset-12-2-tab">
<div class="sourceCode" id="cb35" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb35-1">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb35-2">  diamonds,</span>
<span id="cb35-3">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb35-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span>,</span>
<span id="cb35-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">greeting =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>,</span>
<span id="cb35-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data_description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_data_description.md"</span></span>
<span id="cb35-7">)</span></code></pre></div>
</div>
</div>
</div></li>
<li><p><code>extra_instructions</code></p>
<p>For further tweaking the LLMs behaviour you can use <code>extra_instructions</code>. You can go nuts here: make it talk like a pirate, use an emoji in every sentence, or use an annoying amount of diamond-related phrases. You can also use this section for more practical guidance like notes on preferred spelling, tone, or handling of sensitive terms. For example:</p>
<div class="sourceCode" id="cb36" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb36-1"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Assume the user doesn't know much about diamonds: </span>
<span id="cb36-2">  keep explanations simple and accessible.</span>
<span id="cb36-3"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>When describing diamond attributes, default to plain English. </span>
<span id="cb36-4">  If a term is highly technical, include a short clarification.</span>
<span id="cb36-5"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Maintain consistent spelling in British English.</span></code></pre></div>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-13-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-13-1" aria-controls="tabset-13-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-13-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-13-2" aria-controls="tabset-13-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-13-1" class="tab-pane active" aria-labelledby="tabset-13-1-tab">
<div class="sourceCode" id="cb37" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb37-1">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(</span>
<span id="cb37-2">    diamonds,</span>
<span id="cb37-3">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb37-4">    client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>,</span>
<span id="cb37-5">    greeting<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>),</span>
<span id="cb37-6">    data_description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_data_description.md"</span>),</span>
<span id="cb37-7">    extra_instructions<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_extra_instructions.md"</span>)</span>
<span id="cb37-8">)</span></code></pre></div>
</div>
<div id="tabset-13-2" class="tab-pane" aria-labelledby="tabset-13-2-tab">
<div class="sourceCode" id="cb38" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb38-1">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb38-2">  diamonds,</span>
<span id="cb38-3">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb38-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span>,</span>
<span id="cb38-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">greeting =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>,</span>
<span id="cb38-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data_description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_data_description.md"</span>,</span>
<span id="cb38-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">extra_instructions =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_extra_instructions.md"</span></span>
<span id="cb38-8">)</span></code></pre></div>
</div>
</div>
</div></li>
<li><p><code>categorical_threshold</code></p>
<p>This threshold applies to text columns, and sets the maximum number of unique values to consider it as a categorical variable. The default is 20.</p></li>
<li><p><code>prompt_template</code></p>
<p>The <code>prompt_template</code> is a more advanced parameter to provide a custom prompt template. If you don’t provide it, <code>querychat</code> will use the built-in prompt, which we’ll inspect a little bit closer later.</p></li>
</ul>
<p>Besides arguments, you can also call methods on the <code>QueryChat</code> object. One of them is <code>cleanup()</code>, which releases any resources (e.g.&nbsp;database connections) associated with the data source. You should call this when you are done using the <code>QueryChat</code> object to avoid resource leaks:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-14-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-14-1" aria-controls="tabset-14-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-14-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-14-2" aria-controls="tabset-14-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-14-1" class="tab-pane active" aria-labelledby="tabset-14-1-tab">
<div class="sourceCode" id="cb39" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb39-1">qc.cleanup()</span></code></pre></div>
</div>
<div id="tabset-14-2" class="tab-pane" aria-labelledby="tabset-14-2-tab">
<div class="sourceCode" id="cb40" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb40-1">qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cleanup</span>()</span></code></pre></div>
</div>
</div>
</div>
<p>That’s… A lot! And all you need to chat safely with your data. As you’ve seen in our earlier examples, you don’t need a lot to get started (<code>data_source</code> and <code>table_name</code> are enough, and in R you can even omit the <code>table_name</code>). But knowing the possibilities makes it easier to customise <code>querychat</code> to your liking.</p>
</section>
</section>
<section id="beyond-chat-bespoke-interfaces" class="level1">
<h1>Beyond chat: bespoke interfaces</h1>
<p>Now you know everything there is to know about the <code>QueryChat</code> object. You know how to add a greeting, additional context, and your favourite LLM. However, it’s time to dream bigger and time to get building! Because chatting with your data safely is one thing, but if you truly want to amaze your users you can build an entire dashboard around it. Plots, maps, tables, and value boxes that all update based on the user’s questions. Your own bespoke interface. Before we dive into that, let’s first take a step back and see if we can reconstruct the “quick launch” app.</p>
<p>You need two things if you want to build a Shiny app with <code>querychat</code>:</p>
<ul>
<li>The UI component (the chat window)</li>
<li>A server method that deals with the results</li>
</ul>
<p>For the UI component, there are two choices: <code>sidebar()</code> or <code>ui()</code>. The difference? <code>ui</code>&nbsp;creates a basic chat interface, while&nbsp;<code>sidebar</code>&nbsp;wraps the chat interface in a&nbsp;(<code>bslib</code>, for the R lovers) sidebar&nbsp;component designed to be used as the&nbsp;<code>sidebar</code>&nbsp;argument to <code>page_sidebar</code>.</p>
<p>If we want to do something with the results that get returned by <code>querychat</code>, we need to make use of the <code>server()</code> method. The server method returns:</p>
<ul>
<li><code>sql</code>: a reactive that returns the current SQL query. And, if you want to run your own queries, you can also call the <code>$sql()</code> method on the <code>QueryChat</code> object to run queries.</li>
<li><code>title</code>: a reactive that returns the current title.</li>
<li><code>df</code>: a reactive that returns the data frame, filtered and sorted by the current SQL query.</li>
</ul>
<p>Let’s take a look at a minimal example that rebuilds the “quick launch” app:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-15-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-15-1" aria-controls="tabset-15-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-15-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-15-2" aria-controls="tabset-15-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-15-1" class="tab-pane active" aria-labelledby="tabset-15-1-tab">
<div class="sourceCode" id="cb41" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb41-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> App, render, ui</span>
<span id="cb41-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> seaborn <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dataset</span>
<span id="cb41-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> querychat <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> QueryChat</span>
<span id="cb41-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb41-5"></span>
<span id="cb41-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb41-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Setup</span></span>
<span id="cb41-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb41-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. Initialize QueryChat with custom files</span></span>
<span id="cb41-10">diamonds <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> load_dataset(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>)</span>
<span id="cb41-11">diamonds_greeting <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span></span>
<span id="cb41-12">diamonds_data_description <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_data_description.md"</span></span>
<span id="cb41-13">diamonds_extra_instructions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_extra_instructions.md"</span></span>
<span id="cb41-14"></span>
<span id="cb41-15">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(</span>
<span id="cb41-16">    diamonds,</span>
<span id="cb41-17">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb41-18">    client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>,</span>
<span id="cb41-19">    greeting<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>diamonds_greeting,</span>
<span id="cb41-20">    data_description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>diamonds_data_description,</span>
<span id="cb41-21">    extra_instructions<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>diamonds_extra_instructions,</span>
<span id="cb41-22">)</span>
<span id="cb41-23"></span>
<span id="cb41-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb41-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># UI</span></span>
<span id="cb41-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb41-27">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_sidebar(</span>
<span id="cb41-28">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. QueryChat sidebar UI component</span></span>
<span id="cb41-29">    qc.sidebar(),</span>
<span id="cb41-30">    ui.card(</span>
<span id="cb41-31">        ui.card_header(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SQL Query"</span>),</span>
<span id="cb41-32">        ui.output_text_verbatim(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sql_output"</span>),</span>
<span id="cb41-33">        fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb41-34">    ),</span>
<span id="cb41-35">    ui.card(</span>
<span id="cb41-36">        ui.card_header(ui.output_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title"</span>)),</span>
<span id="cb41-37">        ui.output_data_frame(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data_table"</span>),</span>
<span id="cb41-38">        fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb41-39">    ),</span>
<span id="cb41-40">    fillable<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb41-41">    theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.Theme.from_brand(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>),</span>
<span id="cb41-42">)</span>
<span id="cb41-43"></span>
<span id="cb41-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb41-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Server</span></span>
<span id="cb41-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb41-47"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb41-48">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. QueryChat server component</span></span>
<span id="cb41-49">    vals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> qc.server()</span>
<span id="cb41-50"></span>
<span id="cb41-51">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. Use the filtered/sorted data frame reactively</span></span>
<span id="cb41-52">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.data_frame</span></span>
<span id="cb41-53">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> data_table():</span>
<span id="cb41-54">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> vals.df()</span>
<span id="cb41-55"></span>
<span id="cb41-56">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb41-57">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> title():</span>
<span id="cb41-58">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> vals.title() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Diamonds"</span></span>
<span id="cb41-59"></span>
<span id="cb41-60">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 5. Display the generated SQL query</span></span>
<span id="cb41-61">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb41-62">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> sql_output():</span>
<span id="cb41-63">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> vals.sql() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SELECT * FROM diamonds;"</span></span>
<span id="cb41-64"></span>
<span id="cb41-65">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
<p>To keep things simple, we opted for a simple verbatim text output, but we also could’ve chosen for this combination, which is from the <a href="https://posit-dev.github.io/shinychat/py/"><code>shinychat</code></a> package:</p>
<div class="sourceCode" id="cb42" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb42-1">ui.output_ui(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sql_output"</span>)</span></code></pre></div>
<div class="sourceCode" id="cb43" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb43-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.ui</span></span>
<span id="cb43-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> sql_output():</span>
<span id="cb43-3">    sql_value <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> vals.sql() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">or</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"SELECT * FROM </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>table_name<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb43-4">    sql_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"```sql</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>sql_value<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">```"</span></span>
<span id="cb43-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> output_markdown_stream(</span>
<span id="cb43-6">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sql_code"</span>,</span>
<span id="cb43-7">        content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>sql_code,</span>
<span id="cb43-8">        auto_scroll<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb43-9">        width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"100%"</span>,</span>
<span id="cb43-10">    )</span></code></pre></div>
<p>This actually happens in the source code for the quick launch app. It would give us the nice “copy to clipboard” feature and nice formatting. Another alternative would be the native <a href="https://shiny.posit.co/py/api/core/ui.output_markdown_stream.html">markdown stream component in Shiny</a>.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
brand.yml
</div>
</div>
<div class="callout-body-container callout-body">
<p>If you want to make use of brand.yml, you need to add a theme argument: <code>theme=ui.Theme.from_brand(**file**)</code>. Make sure you have installed the latest version of shiny with the&nbsp;<code>theme</code>&nbsp;extra! You can simply add it with: <code>uv add "shiny[theme]"</code> (if using <code>uv</code>), or <code>pip install "shiny[theme]"</code></p>
</div>
</div>
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/diamonds-bespoke-py.png" class="img-fluid"></p>
</div>
<div id="tabset-15-2" class="tab-pane" aria-labelledby="tabset-15-2-tab">
<div class="sourceCode" id="cb44" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb44-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shiny)</span>
<span id="cb44-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bslib)</span>
<span id="cb44-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(DT)</span>
<span id="cb44-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(querychat)</span>
<span id="cb44-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb44-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb44-7"></span>
<span id="cb44-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb44-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Setup</span></span>
<span id="cb44-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb44-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. Initialize QueryChat with custom files</span></span>
<span id="cb44-12">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb44-13">  diamonds,</span>
<span id="cb44-14">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds"</span>,</span>
<span id="cb44-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span>,</span>
<span id="cb44-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">greeting =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_greeting.md"</span>,</span>
<span id="cb44-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data_description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_data_description.md"</span>,</span>
<span id="cb44-18">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">extra_instructions =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"diamonds_extra_instructions.md"</span></span>
<span id="cb44-19">)</span>
<span id="cb44-20"></span>
<span id="cb44-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb44-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># UI</span></span>
<span id="cb44-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb44-24">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_sidebar</span>(</span>
<span id="cb44-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Diamonds Explorer"</span>,</span>
<span id="cb44-26">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. QueryChat sidebar UI component</span></span>
<span id="cb44-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sidebar =</span> qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sidebar</span>(),</span>
<span id="cb44-28">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb44-29">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SQL Query"</span>),</span>
<span id="cb44-30">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">verbatimTextOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sql_query"</span>)</span>
<span id="cb44-31">  ),</span>
<span id="cb44-32">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb44-33">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title"</span>)),</span>
<span id="cb44-34">    DT<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">DTOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data_table"</span>)</span>
<span id="cb44-35">  )</span>
<span id="cb44-36">)</span>
<span id="cb44-37"></span>
<span id="cb44-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb44-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Server</span></span>
<span id="cb44-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb44-41">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb44-42">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. QueryChat server component</span></span>
<span id="cb44-43">  vals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">server</span>()</span>
<span id="cb44-44"></span>
<span id="cb44-45">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 3. Display generated SQL query</span></span>
<span id="cb44-46">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>sql_query <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderText</span>({</span>
<span id="cb44-47">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(vals<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sql</span>())) {</span>
<span id="cb44-48">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SELECT * FROM diamonds;"</span>)</span>
<span id="cb44-49">    }</span>
<span id="cb44-50">    vals<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sql</span>()</span>
<span id="cb44-51">  })</span>
<span id="cb44-52"></span>
<span id="cb44-53">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 4. Display data table based on user query</span></span>
<span id="cb44-54">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>data_table <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> DT<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderDT</span>({</span>
<span id="cb44-55">    vals<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">df</span>()</span>
<span id="cb44-56">  })</span>
<span id="cb44-57"></span>
<span id="cb44-58">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 5. Dynamic title based on user query</span></span>
<span id="cb44-59">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>title <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderText</span>({</span>
<span id="cb44-60">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(vals<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span>())) {</span>
<span id="cb44-61">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Diamonds Data"</span>)</span>
<span id="cb44-62">    }</span>
<span id="cb44-63">    vals<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span>()</span>
<span id="cb44-64">  })</span>
<span id="cb44-65">}</span>
<span id="cb44-66"></span>
<span id="cb44-67"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/diamonds-bespoke-r.png" class="img-fluid"></p>
</div>
</div>
</div>
<p>Looks pretty similar to the quick launch app, right?! So that’s how it was build. Note that there a few aesthetic differences though. The quick launch app has a few extra sparks here and there, and our app makes use of custom theming with <code>brand.yml</code>.</p>
<p>So far in our diamonds adventure we have only looked at a simple table, but we can extent this idea much further and build an entire dashboard around it: value boxes, graphs, tables, maps, you name it! This is also what <a href="https://shiny.posit.co/py/templates/sidebot/">sidebot</a> does, and this template is available to get you started quickly. A nice touch is the inclusion of the ✨ icon, which sends a screenshot of the visuals to the LLM for an explanation. How cool is that!</p>
</section>
<section id="adding-querychat-to-your-existing-shiny-app" class="level1">
<h1>Adding querychat to your existing Shiny app</h1>
<p>The idea of <a href="https://shiny.posit.co/py/templates/sidebot/">sidebot</a> is certainly interesting: why build a dashboard with all kind of filters when you can just add a chat window with access to a smart LLM. You ask it questions, <code>querychat</code> returns some SQL and reactive filtered data, and you make sure you update the entire dashboard. Unlimited filter possibilities. And it doesn’t have to be complicated to achieve that.</p>
<p>To demonstrate how easy it is, we are going to use an existing dashboard (SheScores), that currently has a number of filters in it: a slider for the year(s), a dropdown for the continent where the matches took place, the tournaments that took place on those continents, and a switch that filters the data to include only data with known scorers, or not.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/shescores-original-py.gif" class="img-fluid figure-img"></p>
<figcaption>Python version of SheScores</figcaption>
</figure>
</div>
<p>So what does SheScores look like behind the scenes? We’re not going into the nitty gritty details of the SheScores dashboard, and we don’t have to if we want to add <code>querychat</code> to it. The most important bit of logic is stored in a reactive that contains the filtered data. It reacts to changes in any of the inputs (year, continent, tournament, scorer only or not).</p>
<p>The reactive, <code>filtered_data()</code>, forms the basis for all the elements in the dashboard: the value boxes, the map, the graph, and the table.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-16-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-16-1" aria-controls="tabset-16-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-16-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-16-2" aria-controls="tabset-16-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-16-1" class="tab-pane active" aria-labelledby="tabset-16-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>See <a href="https://github.com/hypebright/shescores-dashboard/blob/68f34785f3217d005497f4719b1f5c64af00ac4d/Python/shescores-app.py">GitHub</a> for the full source code.</p>
</div>
</div>
<div class="sourceCode" id="cb45" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb45-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb45-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Setup</span></span>
<span id="cb45-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb45-4">results_with_scorers <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.read_csv(</span>
<span id="cb45-5">    Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent.parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/results_with_scorers.csv"</span></span>
<span id="cb45-6">)</span>
<span id="cb45-7"></span>
<span id="cb45-8">results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.to_datetime(results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>])</span>
<span id="cb45-9"></span>
<span id="cb45-10">results_with_scorers <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> results_with_scorers[</span>
<span id="cb45-11">    (results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tournament"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Friendly"</span>)</span>
<span id="cb45-12">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> (results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2000-01-01"</span>)</span>
<span id="cb45-13">]</span>
<span id="cb45-14"></span>
<span id="cb45-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb45-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># UI</span></span>
<span id="cb45-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb45-18">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_sidebar(</span>
<span id="cb45-19">    ui.sidebar(</span>
<span id="cb45-20">        ui.h4(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Filters"</span>),</span>
<span id="cb45-21">        ui.input_slider(</span>
<span id="cb45-22">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year_filter"</span>,</span>
<span id="cb45-23">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Select year range:"</span>,</span>
<span id="cb45-24">            <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>].dt.year.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>()),</span>
<span id="cb45-25">            <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>].dt.year.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>()),</span>
<span id="cb45-26">            value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[</span>
<span id="cb45-27">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>].dt.year.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>()),</span>
<span id="cb45-28">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>].dt.year.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>()),</span>
<span id="cb45-29">            ],</span>
<span id="cb45-30">            sep<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>,</span>
<span id="cb45-31">        ),</span>
<span id="cb45-32">        ui.input_selectize(</span>
<span id="cb45-33">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"continent_filter"</span>,</span>
<span id="cb45-34">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Select continents:"</span>,</span>
<span id="cb45-35">            choices<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(</span>
<span id="cb45-36">                results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"continent"</span>].dropna().unique().tolist()</span>
<span id="cb45-37">            ),</span>
<span id="cb45-38">            selected<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Europe"</span>,</span>
<span id="cb45-39">            multiple<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb45-40">        ),</span>
<span id="cb45-41">        ui.input_select(</span>
<span id="cb45-42">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tournament_filter"</span>,</span>
<span id="cb45-43">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Select tournaments:"</span>,</span>
<span id="cb45-44">            choices<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[],</span>
<span id="cb45-45">            selected<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[],</span>
<span id="cb45-46">            multiple<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb45-47">        ),</span>
<span id="cb45-48">        ui.input_switch(</span>
<span id="cb45-49">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scorer_only"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Show matches with scorer data only"</span>, value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span></span>
<span id="cb45-50">        ),</span>
<span id="cb45-51">        width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"30%"</span>,</span>
<span id="cb45-52">    ),</span>
<span id="cb45-53">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other UI content</span></span>
<span id="cb45-54">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb45-55">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"She Scores ⚽️: Women's International Soccer Matches"</span>,</span>
<span id="cb45-56">    fillable<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb45-57">    theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.Theme.from_brand(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>),</span>
<span id="cb45-58">)</span>
<span id="cb45-59"></span>
<span id="cb45-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb45-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Server</span></span>
<span id="cb45-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb45-63"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb45-64">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reactive filtered data based on inputs</span></span>
<span id="cb45-65">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.calc</span></span>
<span id="cb45-66">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> filtered_data():</span>
<span id="cb45-67">        req(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.continent_filter()) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb45-68">        req(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.tournament_filter()) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb45-69"></span>
<span id="cb45-70">        data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> results_with_scorers.copy()</span>
<span id="cb45-71">        data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> data[</span>
<span id="cb45-72">            (data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>].dt.year <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.year_filter()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>])</span>
<span id="cb45-73">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> (data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>].dt.year <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.year_filter()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb45-74">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> (data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"continent"</span>].isin(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.continent_filter()))</span>
<span id="cb45-75">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> (data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tournament"</span>].isin(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.tournament_filter()))</span>
<span id="cb45-76">        ]</span>
<span id="cb45-77"></span>
<span id="cb45-78">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.scorer_only():</span>
<span id="cb45-79">            data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> data[data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scorer"</span>].notna()]</span>
<span id="cb45-80"></span>
<span id="cb45-81">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> data</span>
<span id="cb45-82">    </span>
<span id="cb45-83">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other server logic</span></span>
<span id="cb45-84">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb45-85">    </span>
<span id="cb45-86">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
</div>
<div id="tabset-16-2" class="tab-pane" aria-labelledby="tabset-16-2-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>Check out the full source code on <a href="https://github.com/hypebright/shescores-dashboard/blob/9c8b20d64adfb67566272c587e158dbf2a5052d8/R/shescores-app.R">GitHub</a>.</p>
</div>
</div>
<div class="sourceCode" id="cb46" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb46-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb46-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Setup</span></span>
<span id="cb46-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb46-4">results_with_scorers <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read.csv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"../data/results_with_scorers.csv"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb46-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(tournament <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Friendly"</span>, date <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2000-01-01"</span>)</span>
<span id="cb46-6"></span>
<span id="cb46-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other setup</span></span>
<span id="cb46-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb46-9"></span>
<span id="cb46-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb46-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># UI</span></span>
<span id="cb46-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb46-13">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_sidebar</span>(</span>
<span id="cb46-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fillable =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb46-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"She Scores ⚽️: Women's International Soccer Matches"</span>,</span>
<span id="cb46-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sidebar =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sidebar</span>(</span>
<span id="cb46-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Filters"</span>,</span>
<span id="cb46-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"30%"</span>,</span>
<span id="cb46-19">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Year filter</span></span>
<span id="cb46-20">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sliderInput</span>(</span>
<span id="cb46-21">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"year_filter"</span>,</span>
<span id="cb46-22">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Select year range:"</span>,</span>
<span id="cb46-23">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">min =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">year</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(results_with_scorers<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>date))),</span>
<span id="cb46-24">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">max =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">year</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(results_with_scorers<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>date))),</span>
<span id="cb46-25">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb46-26">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">year</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(results_with_scorers<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>date))),</span>
<span id="cb46-27">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">year</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(results_with_scorers<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>date)))</span>
<span id="cb46-28">      ),</span>
<span id="cb46-29">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sep =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span></span>
<span id="cb46-30">    ),</span>
<span id="cb46-31">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Continent filter (dropdown)</span></span>
<span id="cb46-32">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pickerInput</span>(</span>
<span id="cb46-33">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"continent_filter"</span>,</span>
<span id="cb46-34">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Select continents:"</span>,</span>
<span id="cb46-35">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">choices =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sort</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unique</span>(results_with_scorers<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>continent)),</span>
<span id="cb46-36">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">selected =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sort</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unique</span>(results_with_scorers<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>continent)),</span>
<span id="cb46-37">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">options =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pickerOptions</span>(</span>
<span id="cb46-38">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">actionsBox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb46-39">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">selectedTextFormat =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"count &gt; 1"</span>,</span>
<span id="cb46-40">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">countSelectedText =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"{0} continents selected"</span></span>
<span id="cb46-41">      ),</span>
<span id="cb46-42">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">multiple =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb46-43">    ),</span>
<span id="cb46-44">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Tournament filter (dropdown)</span></span>
<span id="cb46-45">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pickerInput</span>(</span>
<span id="cb46-46">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tournament_filter"</span>,</span>
<span id="cb46-47">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Select tournaments:"</span>,</span>
<span id="cb46-48">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">choices =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>,</span>
<span id="cb46-49">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">selected =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>,</span>
<span id="cb46-50">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">options =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pickerOptions</span>(</span>
<span id="cb46-51">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">actionsBox =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb46-52">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">liveSearch =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb46-53">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">liveSearchPlaceholder =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Search for a tournament"</span>,</span>
<span id="cb46-54">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">selectedTextFormat =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"count &gt; 1"</span>,</span>
<span id="cb46-55">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">countSelectedText =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"{0} tournaments selected"</span></span>
<span id="cb46-56">      ),</span>
<span id="cb46-57">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">multiple =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb46-58">    ),</span>
<span id="cb46-59">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Switch to show data with scorers only</span></span>
<span id="cb46-60">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">input_switch</span>(</span>
<span id="cb46-61">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">id =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scorer_only"</span>,</span>
<span id="cb46-62">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Show matches with scorer data only"</span>,</span>
<span id="cb46-63">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb46-64">    ),</span>
<span id="cb46-65">  )</span>
<span id="cb46-66">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other UI content</span></span>
<span id="cb46-67">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb46-68">)</span>
<span id="cb46-69"></span>
<span id="cb46-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb46-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Server</span></span>
<span id="cb46-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb46-73">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb46-74">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reactive filtered data based on inputs</span></span>
<span id="cb46-75">  filtered_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reactive</span>({</span>
<span id="cb46-76">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>continent_filter) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb46-77">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tournament_filter) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb46-78"></span>
<span id="cb46-79">    results_with_scorers <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb46-80">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">date =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.Date</span>(date)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb46-81">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(</span>
<span id="cb46-82">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">year</span>(date) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>year_filter[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],</span>
<span id="cb46-83">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">year</span>(date) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>year_filter[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>],</span>
<span id="cb46-84">        continent <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%in%</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>continent_filter,</span>
<span id="cb46-85">        tournament <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%in%</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tournament_filter,</span>
<span id="cb46-86">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>scorer_only) {</span>
<span id="cb46-87">          <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.na</span>(scorer)</span>
<span id="cb46-88">        } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> {</span>
<span id="cb46-89">          <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb46-90">        }</span>
<span id="cb46-91">      )</span>
<span id="cb46-92">  })</span>
<span id="cb46-93"></span>
<span id="cb46-94">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other server logic</span></span>
<span id="cb46-95">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb46-96">}</span>
<span id="cb46-97"></span>
<span id="cb46-98"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
</div>
</div>
</div>
<p>Now we want to get rid of all those filters. We want a chat window instead. What do we need to change in order to use <code>querychat</code>? Spoiler alert: not much.</p>
<p>Of course we need to initialise our <code>QueryChat</code> object. And since we’re not talking about diamonds, we need to make sure to provide a proper soccer-themed greeting, a data description, and extra instructions:</p>
<p><code>shescores_greeting.md</code>:</p>
<div class="sourceCode" id="cb47" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb47-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Welcome to SheScores! ⚽</span></span>
<span id="cb47-2"></span>
<span id="cb47-3">I'm here to help you explore international women's soccer match data. </span>
<span id="cb47-4">You can ask me to filter and sort the dashboard, answer questions about the data, </span>
<span id="cb47-5">or provide insights about teams, players, tournaments, and more.</span>
<span id="cb47-6"></span>
<span id="cb47-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Here are some ideas to get started:</span></span>
<span id="cb47-8"></span>
<span id="cb47-9">**Explore match data:**</span>
<span id="cb47-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Show me the highest-scoring matches in World Cup tournaments&lt;/span&gt;</span>
<span id="cb47-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Which teams have played the most matches against each other?&lt;/span&gt;</span>
<span id="cb47-12"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Filter to matches from the 2025 UEFA Euro&lt;/span&gt;</span>
<span id="cb47-13"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Which team has the best win rate in the Canada vs United States rivalry?&lt;/span&gt;</span>
<span id="cb47-14">  </span>
<span id="cb47-15">**Analyze player performance:**</span>
<span id="cb47-16"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Who are the top scorers in World Cup history?&lt;/span&gt;</span>
<span id="cb47-17"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Which players have scored the most penalty goals?&lt;/span&gt;</span>
<span id="cb47-18"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  * </span>&lt;span class="suggestion"&gt;Which matches had the most own goals?&lt;/span&gt;</span>
<span id="cb47-19"></span>
<span id="cb47-20">What would you like to explore?</span></code></pre></div>
<p><code>shescores_data_description.md</code>:</p>
<div class="sourceCode" id="cb48" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb48-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Dataset description</span></span>
<span id="cb48-2">This dataset contains international women’s football match results. </span>
<span id="cb48-3">It includes match metadata (date, location, teams), outcomes (scores), </span>
<span id="cb48-4">plus optional event-level information such as individual scorers. </span>
<span id="cb48-5">Not all friendly matches are represented; </span>
<span id="cb48-6">major tournaments are mostly complete.</span>
<span id="cb48-7"></span>
<span id="cb48-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Column descriptions</span></span>
<span id="cb48-9"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>date (string, YYYY-MM-DD): The calendar date on which the match was played.</span>
<span id="cb48-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>home_team (string): Name of the home team.</span>
<span id="cb48-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>date (string, YYYY-MM-DD): The calendar date on which the match was played.</span>
<span id="cb48-12"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>home_team (string): Name of the home team.</span>
<span id="cb48-13"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>away_team (string): Name of the away team.</span>
<span id="cb48-14"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>home_score (integer): Goals scored by the home team at full time (extra time included, - penalty shoot-outs excluded).</span>
<span id="cb48-15"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>away_score (integer): Goals scored by the away team at full time (extra time included, - penalty shoot-outs excluded).</span>
<span id="cb48-16"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>tournament (string): Name of the competition or event.</span>
<span id="cb48-17"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>city (string): City or administrative area where the match was played.</span>
<span id="cb48-18"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>country (string): Country where the match was played.</span>
<span id="cb48-19"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>neutral (boolean): Indicates whether the match took place at a neutral venue.</span>
<span id="cb48-20"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>team (string, optional): Team associated with a recorded scoring event.</span>
<span id="cb48-21"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>scorer (string, optional): Player who scored the goal.</span>
<span id="cb48-22"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>minute (integer, optional): Match minute in which the goal occurred.</span>
<span id="cb48-23"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>own_goal (boolean, optional): Indicates whether the goal was an own goal.</span>
<span id="cb48-24"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>penalty (boolean, optional): Indicates whether the goal was scored from a penalty kick.</span>
<span id="cb48-25"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>country_flag_home (string): Emoji or symbol representing the home country.</span>
<span id="cb48-26"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>country_flag_away (string): Emoji or symbol representing the away country.</span>
<span id="cb48-27"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>continent (string): Continent associated with the home country.</span>
<span id="cb48-28"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>country_code (string): Country code associated with the home team (e.g., ISO-like).</span>
<span id="cb48-29"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>latitude (float): Latitude of the match location.</span>
<span id="cb48-30"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>longitude (float): Longitude of the match location.</span>
<span id="cb48-31"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>match_id (string): Unique identifier for the match, typically based on date and team names.</span></code></pre></div>
<p><code>shescores_extra_instrucions.md</code>:</p>
<div class="sourceCode" id="cb49" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb49-1"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Maintain consistent spelling in British English.</span>
<span id="cb49-2"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Don't add any extra columns to the dataset. You may use them internally </span>
<span id="cb49-3">  for calculations, but the final output should only include the original </span>
<span id="cb49-4">  columns with the original column names.</span>
<span id="cb49-5"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Soccer terminology should be used throughout the analysis </span>
<span id="cb49-6">  (e.g., "goal" instead of "point").</span></code></pre></div>
<p>Now, adding <code>querychat</code> into the mix is as simple as replacing our inputs in the sidebar with the <code>querychat</code> sidebar component (<code>sidebar()</code>), and our reactive with the results of <code>server()</code>.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-17-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-17-1" aria-controls="tabset-17-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-17-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-17-2" aria-controls="tabset-17-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-17-1" class="tab-pane active" aria-labelledby="tabset-17-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>See <a href="https://github.com/hypebright/shescores-dashboard/blob/963d2b72c600ee9f30ce04da170b05a01c1dc31c/Python/shescores-querychat-app.py">GitHub</a> for the full source code</p>
</div>
</div>
<div class="sourceCode" id="cb50" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb50-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb50-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Setup</span></span>
<span id="cb50-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb50-4">results_with_scorers <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.read_csv(</span>
<span id="cb50-5">    Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent.parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/results_with_scorers.csv"</span></span>
<span id="cb50-6">)</span>
<span id="cb50-7"></span>
<span id="cb50-8">results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.to_datetime(results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>])</span>
<span id="cb50-9"></span>
<span id="cb50-10">results_with_scorers <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> results_with_scorers[</span>
<span id="cb50-11">    (results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tournament"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Friendly"</span>)</span>
<span id="cb50-12">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> (results_with_scorers[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"date"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2000-01-01"</span>)</span>
<span id="cb50-13">]</span>
<span id="cb50-14"></span>
<span id="cb50-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. Initialize QueryChat with custom files</span></span>
<span id="cb50-16">shescores_greeting <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_greeting.md"</span></span>
<span id="cb50-17">shescores_data_description <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_data_description.md"</span></span>
<span id="cb50-18">shescores_extra_instructions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_extra_instructions.md"</span></span>
<span id="cb50-19"></span>
<span id="cb50-20">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(</span>
<span id="cb50-21">    results_with_scorers,</span>
<span id="cb50-22">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results_with_scorers"</span>,</span>
<span id="cb50-23">    client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>,</span>
<span id="cb50-24">    greeting<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shescores_greeting,</span>
<span id="cb50-25">    data_description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shescores_data_description,</span>
<span id="cb50-26">    extra_instructions<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shescores_extra_instructions,</span>
<span id="cb50-27">)</span>
<span id="cb50-28"></span>
<span id="cb50-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other setup</span></span>
<span id="cb50-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb50-31"></span>
<span id="cb50-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb50-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># UI</span></span>
<span id="cb50-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb50-35">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_sidebar(</span>
<span id="cb50-36">    qc.sidebar(),</span>
<span id="cb50-37">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other UI components</span></span>
<span id="cb50-38">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb50-39">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"She Scores ⚽️: Women's International Soccer Matches"</span>,</span>
<span id="cb50-40">    fillable<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb50-41">    theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.Theme.from_brand(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>),</span>
<span id="cb50-42">)</span>
<span id="cb50-43"></span>
<span id="cb50-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb50-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Server</span></span>
<span id="cb50-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb50-47"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb50-48">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reactive filtered data based on query</span></span>
<span id="cb50-49">    filtered_data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> qc.server()</span>
<span id="cb50-50">    </span>
<span id="cb50-51">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other server logic</span></span>
<span id="cb50-52">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb50-53">    </span>
<span id="cb50-54">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
</div>
<div id="tabset-17-2" class="tab-pane" aria-labelledby="tabset-17-2-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>Check out the full code on <a href="https://github.com/hypebright/shescores-dashboard/blob/9c8b20d64adfb67566272c587e158dbf2a5052d8/R/shescores-querychat-app.R">GitHub</a>.</p>
</div>
</div>
<div class="sourceCode" id="cb51" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb51-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb51-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Setup</span></span>
<span id="cb51-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb51-4">results_with_scorers <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read.csv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"../data/results_with_scorers.csv"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb51-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(tournament <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Friendly"</span>, date <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2000-01-01"</span>)</span>
<span id="cb51-6"></span>
<span id="cb51-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. Initialize QueryChat with custom files</span></span>
<span id="cb51-8">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb51-9">  results_with_scorers,</span>
<span id="cb51-10">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results_with_scorers"</span>,</span>
<span id="cb51-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span>,</span>
<span id="cb51-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">greeting =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_greeting.md"</span>,</span>
<span id="cb51-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data_description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_data_description.md"</span>,</span>
<span id="cb51-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">extra_instructions =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_extra_instructions.md"</span></span>
<span id="cb51-15">)</span>
<span id="cb51-16"></span>
<span id="cb51-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other setup</span></span>
<span id="cb51-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb51-19"></span>
<span id="cb51-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb51-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># UI</span></span>
<span id="cb51-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb51-23">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_sidebar</span>(</span>
<span id="cb51-24">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fillable =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb51-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"She Scores ⚽️: Women's International Soccer Matches"</span>,</span>
<span id="cb51-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sidebar =</span> qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sidebar</span>(),</span>
<span id="cb51-27">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other UI components</span></span>
<span id="cb51-28">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb51-29">)</span>
<span id="cb51-30"></span>
<span id="cb51-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb51-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Server</span></span>
<span id="cb51-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ===============================</span></span>
<span id="cb51-34">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb51-35">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reactive filtered data based on query</span></span>
<span id="cb51-36">  filtered_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">server</span>()</span>
<span id="cb51-37"></span>
<span id="cb51-38">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other server logic</span></span>
<span id="cb51-39">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb51-40">  </span>
<span id="cb51-41">}</span>
<span id="cb51-42"></span>
<span id="cb51-43"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
</div>
</div>
</div>
<p>It results in a lot less code and logic too. Win-win. Thanks <code>querychat</code> !</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/querychat-python-r/shescores-querychat-py.gif" class="img-fluid figure-img"></p>
<figcaption>Python version of SheScores with querychat</figcaption>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>While we don’t have a reset button in the app, <code>querychat</code> knows very well what to do when you ask it to reset the dashboard. In this case, it will display the unfiltered data, just like we started when we launched the app.</p>
</div>
</div>
</section>
<section id="database-options" class="level1">
<h1>Database options</h1>
<p>So far we’ve only worked with simple datasets: the&nbsp;<code>diamonds</code>&nbsp;dataset that ships with a package, and our soccer data loaded from a&nbsp;<code>.csv</code>. But here’s how it works under the hood: even in those examples, you weren’t really querying a data frame directly.&nbsp;<code>querychat</code>&nbsp;hands everything off to DuckDB, which becomes the engine that executes all generated SQL. And DuckDB does so quickly and efficiently. Your data frame or&nbsp;<code>.csv</code>&nbsp;is effectively registered inside DuckDB, and every answer comes from real SQL running on that engine.</p>
<p>But what if you don’t want to work with in-memory tables at all? What if you already have a database you want to query directly? Maybe a DuckDB file, a SQLite database, Postgres, or even BigQuery? That’s exactly what the&nbsp;<code>data_source</code>&nbsp;argument is for. Earlier we used it with plain data frames, but it also accepts database connections. In Python, that means any <a href="https://www.sqlalchemy.org">SQLAlchemy-supported database</a>; in R, anything that <a href="https://dbi.r-dbi.org"><code>DBI</code></a> can handle.&nbsp;<code>querychat</code>&nbsp;will inspect the schema of whatever you connect, and from that moment on the workflow is identical as before, only now you’re interacting with your own database.</p>
<p>Let’s take a look at how to set up&nbsp;<code>querychat</code>&nbsp;with another backend (SQLite) using the&nbsp;<code>data_source</code>&nbsp;argument.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-18-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-18-1" aria-controls="tabset-18-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-18-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-18-2" aria-controls="tabset-18-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-18-1" class="tab-pane active" aria-labelledby="tabset-18-1-tab">
<p>For demonstration purposes, we’ll create a SQLite database from the SheScores data (<code>results_with_scorers.csv</code>).</p>
<div class="sourceCode" id="cb52" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb52-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb52-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sqlalchemy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> create_engine</span>
<span id="cb52-3"></span>
<span id="cb52-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># From results_with_scorers.csv, create a SQLite database named shescores.db</span></span>
<span id="cb52-5">df_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent.parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/results_with_scorers.csv"</span></span>
<span id="cb52-6"></span>
<span id="cb52-7">df <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.read_csv(df_path)</span>
<span id="cb52-8"></span>
<span id="cb52-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create the SQLite database and store the DataFrame in it</span></span>
<span id="cb52-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Save database in top-level /data directory</span></span>
<span id="cb52-11">df.to_sql(</span>
<span id="cb52-12">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results_with_scorers"</span>,</span>
<span id="cb52-13">    con<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>create_engine(</span>
<span id="cb52-14">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sqlite:///"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent.parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/shescores.db"</span>)</span>
<span id="cb52-15">    ),</span>
<span id="cb52-16">    if_exists<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>,</span>
<span id="cb52-17">    index<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb52-18">)</span></code></pre></div>
<p>We can then use this database in our <code>QueryChat</code> instance like so:</p>
<div class="sourceCode" id="cb53" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb53-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb53-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> sqlalchemy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> create_engine</span>
<span id="cb53-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> querychat <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> QueryChat</span>
<span id="cb53-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb53-5"></span>
<span id="cb53-6">load_dotenv()</span>
<span id="cb53-7"></span>
<span id="cb53-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Custom files for SheScores</span></span>
<span id="cb53-9">shescores_greeting <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_greeting.md"</span></span>
<span id="cb53-10">shescores_data_description <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_data_description.md"</span></span>
<span id="cb53-11">shescores_extra_instructions <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_extra_instructions.md"</span></span>
<span id="cb53-12"></span>
<span id="cb53-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Now create a QueryChat instance to interact with the database</span></span>
<span id="cb53-14">db_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent.parent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/shescores.db"</span></span>
<span id="cb53-15">engine <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> create_engine(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"sqlite:///</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>db_path<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb53-16"></span>
<span id="cb53-17">qc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> QueryChat(</span>
<span id="cb53-18">    engine,</span>
<span id="cb53-19">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results_with_scorers"</span>,</span>
<span id="cb53-20">    client<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anthropic/claude-sonnet-4-5"</span>,</span>
<span id="cb53-21">    greeting<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shescores_greeting,</span>
<span id="cb53-22">    data_description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shescores_data_description,</span>
<span id="cb53-23">    extra_instructions<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shescores_extra_instructions,</span>
<span id="cb53-24">)</span>
<span id="cb53-25"></span>
<span id="cb53-26">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> qc.app()</span></code></pre></div>
<p>You can also create a DuckDB database from a CSV file or a pandas DataFrame, which is definitely nice for larger datasets. For more examples you can check out the package documentation on <a href="https://posit-dev.github.io/querychat/py/data-sources.html">data sources</a>.</p>
<p>Even if you have a database that isn’t supported by SQLAlchemy or isn’t suited for DuckDB, you can still let <code>querychat</code> access it. In that case, you need to implement the <a href="https://posit-dev.github.io/querychat/py/reference/types.DataSource.html">DataSource</a> interface/protocol.</p>
</div>
<div id="tabset-18-2" class="tab-pane" aria-labelledby="tabset-18-2-tab">
<p>For demonstration purposes, we’ll create a SQLite database from the SheScores data (<code>results_with_scorers.csv</code>). To create a new SQLite database, you simply supply the filename to&nbsp;<a href="https://dbi.r-dbi.org/reference/dbConnect.html"><code>dbConnect()</code></a>. And with <code>dbWriteTable(</code>), you can easily copy an R dataframe into that newly generated SQLite database:</p>
<div class="sourceCode" id="cb54" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb54-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(DBI)</span>
<span id="cb54-2"></span>
<span id="cb54-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># From results_with_scorers.csv, create a SQLite database named shescores.db</span></span>
<span id="cb54-4">results_with_scorers <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read.csv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/results_with_scorers.csv"</span>)</span>
<span id="cb54-5"></span>
<span id="cb54-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create a connection to a new SQLite database</span></span>
<span id="cb54-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Save database in top-level /data directory</span></span>
<span id="cb54-8">conn <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbConnect</span>(RSQLite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SQLite</span>(), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/shescores.db"</span>)</span>
<span id="cb54-9"></span>
<span id="cb54-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Write the data frame to a table named results_with_scorers</span></span>
<span id="cb54-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbWriteTable</span>(</span>
<span id="cb54-12">  conn,</span>
<span id="cb54-13">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results_with_scorers"</span>,</span>
<span id="cb54-14">  results_with_scorers,</span>
<span id="cb54-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">overwrite =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb54-16">)</span>
<span id="cb54-17"></span>
<span id="cb54-18"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbDisconnect</span>(conn)</span></code></pre></div>
<p>If you have a SQLite database, connecting to it works in the same manner:</p>
<div class="sourceCode" id="cb55" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb55-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(querychat)</span>
<span id="cb55-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(DBI)</span>
<span id="cb55-3"></span>
<span id="cb55-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create a connection to a SQLite database</span></span>
<span id="cb55-5">conn <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbConnect</span>(RSQLite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SQLite</span>(), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data/shescores.db"</span>)</span>
<span id="cb55-6"></span>
<span id="cb55-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Write the data frame to a table named results_with_scorers</span></span>
<span id="cb55-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dbWriteTable</span>(</span>
<span id="cb55-9">  conn,</span>
<span id="cb55-10">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results_with_scorers"</span>,</span>
<span id="cb55-11">  results_with_scorers,</span>
<span id="cb55-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">overwrite =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb55-13">)</span>
<span id="cb55-14"></span>
<span id="cb55-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Now create a QueryChat instance to interact with the database</span></span>
<span id="cb55-16">qc <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> QueryChat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(</span>
<span id="cb55-17">  conn,</span>
<span id="cb55-18">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results_with_scorers"</span>,</span>
<span id="cb55-19">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">client =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude/claude-sonnet-4-5"</span>,</span>
<span id="cb55-20">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">greeting =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_greeting.md"</span>,</span>
<span id="cb55-21">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data_description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_data_description.md"</span>,</span>
<span id="cb55-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">extra_instructions =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shescores_extra_instructions.md"</span></span>
<span id="cb55-23">)</span>
<span id="cb55-24"></span>
<span id="cb55-25">qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">app</span>()</span></code></pre></div>
<p>Looking for more examples? Check out these <a href="https://github.com/posit-dev/querychat/tree/main/pkg-r/inst/examples-shiny/sqlite">database setup examples for querychat</a>.</p>
</div>
</div>
</div>
<p><code>querychat</code> knows how to deal with databases, and it has some convenient features for it too, especially when things go wrong: it validates whether tables actually exist and handles any issues gracefully (without cryptic error messages).</p>
<p>One thing to keep in mind when you move from in-memory data to real databases, especially inside Shiny apps, is proper connection management. Whenever your app opens a database connection, it also needs to close it. In Python that usually means calling&nbsp;<code>engine.dispose()</code>&nbsp;when the app shuts down. In R you would use&nbsp;<code>dbDisconnect(conn)</code>, or rely on a connection pool. SQLAlchemy already provides pooling on the Python side, but in R you’ll want the&nbsp;<code>pool</code>&nbsp;package to handle this in a nice manner.</p>
</section>
<section id="for-the-curious-how-does-querychat-know-what-to-do" class="level1">
<h1>For the curious: how does querychat know what to do?</h1>
<p>You’ve seen what <code>querychat</code> can do, and you know a bit how it works conceptually. But behind all those concepts is of course some real code. So, for the curious amongst us, here’s a little peek into the <code>querychat</code> code!</p>
<p>To talk with an LLM you need a good prompt: prompt design is crucial for a good outcome. A prompt contains context and instructions that an LLM will use to come up with its answer. <code>querychat</code> has a set of instructions for the LLM too, the system prompt, which is stored in a Markdown file (<code>prompt.md</code>).</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-19-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-19-1" aria-controls="tabset-19-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-19-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-19-2" aria-controls="tabset-19-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-19-1" class="tab-pane active" aria-labelledby="tabset-19-1-tab">
<p>You can check out the <code>prompt.md</code> file <a href="https://github.com/posit-dev/querychat/blob/fea52e4e2b56a2cc0a042140dbe5ce194aca8ac6/pkg-py/src/querychat/prompts/prompt.md">here</a>, or you can simply print it out:</p>
<div class="sourceCode" id="cb56" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb56-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(qc.system_prompt)</span></code></pre></div>
</div>
<div id="tabset-19-2" class="tab-pane" aria-labelledby="tabset-19-2-tab">
<p>You can check out the <code>prompt.md</code> file <a href="https://github.com/posit-dev/querychat/blob/main/pkg-r/inst/prompts/prompt.md">here</a>, or you can simply print it out:</p>
<div class="sourceCode" id="cb57" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb57-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(qc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">system_prompt</span>())</span></code></pre></div>
</div>
</div>
</div>
<p>So, what’s in this prompt? Let’s highlight a few bits:</p>
<div class="sourceCode" id="cb58" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb58-1">You have access to a {{db_type}} SQL database with the following schema:</span>
<span id="cb58-2"></span>
<span id="cb58-3">&lt;database_schema&gt;</span>
<span id="cb58-4">{{schema}}</span>
<span id="cb58-5">&lt;/database_schema&gt;</span>
<span id="cb58-6"></span>
<span id="cb58-7">{{#data_description}}</span>
<span id="cb58-8">Here is additional information about the data:</span>
<span id="cb58-9"></span>
<span id="cb58-10">&lt;data_description&gt;</span>
<span id="cb58-11">{{data_description}}</span>
<span id="cb58-12">&lt;/data_description&gt;</span>
<span id="cb58-13">{{/data_description}}</span>
<span id="cb58-14"></span>
<span id="cb58-15">For security reasons, you may only query this specific table.</span></code></pre></div>
<div class="sourceCode" id="cb59" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb59-1">{{#extra_instructions}}</span>
<span id="cb59-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Additional Instructions</span></span>
<span id="cb59-3"></span>
<span id="cb59-4">{{extra_instructions}}</span>
<span id="cb59-5">{{/extra_instructions}}</span></code></pre></div>
<p>The prompt is a <a href="https://mustache.github.io">Mustache</a> template. It’s a fill-in-the-blanks template: the&nbsp;<code>{name}</code>&nbsp;parts get replaced with real values, and the&nbsp;<code>{#something} ... {{/something}}</code>&nbsp;blocks only appear if that “something” actually exists. When you call QueryChat with corresponding arguments, everything gets filled in.</p>
<p>We talked about tool calling earlier, and there was a little note that said that there’s not just one tool. There are multiple, for different tasks. You can see that back clearly in the prompt, where we instruct the LLM to call a certain tool (e.g.&nbsp;<code>querychat_update_dashboard</code>) when it receives a request:</p>
<div class="sourceCode" id="cb60" style="background: #f1f3f5;"><pre class="sourceCode md code-with-copy"><code class="sourceCode markdown"><span id="cb60-1"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You can handle three types of requests:</span></span>
<span id="cb60-2"></span>
<span id="cb60-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### 1. Filtering and Sorting Data</span></span>
<span id="cb60-4"></span>
<span id="cb60-5">...</span>
<span id="cb60-6"></span>
<span id="cb60-7"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Call <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`querychat_update_dashboard`</span> with the query and a descriptive title</span>
<span id="cb60-8"></span>
<span id="cb60-9">...</span>
<span id="cb60-10"></span>
<span id="cb60-11">The user may ask to "reset" or "start over"; that means clearing the filter and title. Do this by calling querychat_reset_dashboard().</span>
<span id="cb60-12"></span>
<span id="cb60-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### 2. Answering Questions About Data</span></span>
<span id="cb60-14"></span>
<span id="cb60-15">...</span>
<span id="cb60-16"></span>
<span id="cb60-17"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Use the <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`querychat_query`</span> tool to run SQL queries</span>
<span id="cb60-18"></span>
<span id="cb60-19">...</span>
<span id="cb60-20"></span>
<span id="cb60-21"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### 3. Providing Suggestions for Next Steps</span></span>
<span id="cb60-22"></span>
<span id="cb60-23">...</span></code></pre></div>
<p>There are three tools in <code>querychat</code>:</p>
<ul>
<li><code>querychat_query</code>: used whenever the user asks a question that requires data analysis, aggregation, or calculations.</li>
<li><code>querychat_update_dashboard</code>: used whenever the user requests filtering, sorting, or data manipulation on the dashboard with questions like “Show me…” or “Which records have…”. Basically any request that involves showing a subset of the data or reordering it.</li>
<li><code>querychat_reset_dashboard</code>: if the user asks to reset the dashboard</li>
</ul>
<p>All the tools are written as <code>chatlas</code> or <code>ellmer</code> tools. As a user, you don’t have to worry about this though. The LLM makes sure to use the rights tools, which will make sure the SQL gets executed and the data gets filtered accordingly. But hey, this section was for the curious amongst us!</p>
</section>
<section id="safety-control-and-confidence" class="level1">
<h1>Safety, control, and confidence</h1>
<p>At some point, everyone asks the same question: is this safe? And it’s a fair one. Luckily,&nbsp;<code>querychat</code>&nbsp;is designed entirely around control. The LLM never executes anything itself, never touches your data(base) and never sees raw data. Its only job is to propose <em>read-only</em> SQL.</p>
<p>Remember the moment we asked it to drop a table? It refused. Not because it’s polite, but because it’s instructed to do so. Combine that with an underlying database (the built-in DuckDB temporary database or your own) that only provides read-only access, and your data will always be left untouched.&nbsp;</p>
<p>It’s not a black box either: every generated query can be logged, inspected or audited at any time. In Shiny v1.12.0 this becomes even easier thanks to built in OpenTelemetry support via&nbsp;<code>otel</code>. If you’re curious about what that looks like in practice, you can read more in this <a href="https://shiny.posit.co/r/articles/improve/opentelemetry/">article</a>.</p>
<p>The safety, control, and (hopefully) the confidence you’ve gained by now, make it also suitable for enterprise and regulated environments. If you need to use private or managed LLMs, you’re covered: Azure, AWS Bedrock and Google Vertex AI all provide versions of popular models that support tool calling and can work with <code>querychat</code>.</p>
</section>
<section id="other-querychat-apps-in-the-wild" class="level1">
<h1>Other querychat apps in the wild</h1>
<p>It’s always nice to see what others have done with <code>querychat</code>. So here are few sources of inspiration:</p>
<ul>
<li>Do you like trail running? This <a href="https://posit.co/blog/race-stats-dashboard-querychat/">Race Stats dashboard</a> is for you!</li>
<li>Is the American football&nbsp;league more your thing? This <a href="https://www.infoworld.com/article/4040535/chat-with-your-data-the-easy-way-in-r-or-python.html">Shiny for Python app</a> shows you a lot of stats.</li>
<li>Joe Cheng and Garrick Aden-Buie hosted a workshop at posit::conf(2025) called “Programming with LLMs” that also contains some <a href="https://github.com/posit-conf-2025/llm">examples</a>.</li>
<li>And one that we mentioned before: <a href="https://www.infoworld.com/article/4040535/chat-with-your-data-the-easy-way-in-r-or-python.html">sidebot</a>, a dashboard analysing restaurant tipping, which is a template you can use very easily.</li>
</ul>
<p>Whether you’re playing with a small example dataset or building something much bigger, <code>querychat</code> can be the companion in your app that you’re users will love. Build a whole dashboard around the chatbot, or add a touch of LLM magic for those extra side questions. With all this knowledge under your belt, you can build it all!</p>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>While it seems like there is only one tool call, there’s not. In <code>querychat</code> there are different tools for, surprise, different tasks. For the curious there’s a deep dive into <code>querychat</code>’s source code later in this article.↩︎</p></li>
</ol>
</section></div> ]]></description>
  <guid>https://shiny.posit.co/blog/posts/querychat-python-r/</guid>
  <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/querychat-python-r/querychat-python-r-header.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>OpenTelemetry + Shiny for R v1.12</title>
  <dc:creator>Barret Schloerke</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-r-1.12/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-r-1.12/feature-header.png" style="width: 100%; height: auto;">
    </div>
    


<style>
img { border-radius: 8px; }
</style>
<p>We’re thrilled to announce the release of Shiny v1.12! This release brings a powerful new feature that we’ve been working on for months: <strong>built-in OpenTelemetry support</strong>. Whether you’re building small apps or deploying production applications at scale, this release will help you lift the veil on understanding your app’s execution in production.</p>
<div class="callout callout-style-default callout-note no-icon callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon no-icon"></i>
</div>
<div class="callout-title-container flex-fill">
What is Shiny?
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>If you’re new to Shiny, welcome! <a href="../../../r/">Shiny</a> is an R package that makes it easy to build interactive web applications directly from R. You don’t need to be a web developer—if you can write R code, you can create beautiful, interactive dashboards, data explorers, and analytical tools. Shiny handles all the web programming complexity behind the scenes, letting you focus on what you do best: working with data and building analyses.</p>
<p>Since its launch in 2012, Shiny has become the go-to framework for creating data-driven web applications in R, powering everything from internal company dashboards to public-facing data visualization tools. With this latest release, we’re making it easier than ever to understand what’s happening inside your Shiny apps, especially when they’re deployed in production environments.</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-note no-icon callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon no-icon"></i>
</div>
<div class="callout-title-container flex-fill">
What about Shiny for Python?
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>OpenTelemetry support is coming to Shiny for Python! The Shiny team is actively working on bringing the same automatic instrumentation capabilities to Python. This will enable Python developers to gain the same level of observability into their Shiny applications.</p>
<p>Stay tuned for future announcements about OpenTelemetry integration in Shiny for Python. In the meantime, you can follow the development on the <a href="https://github.com/posit-dev/py-shiny">Shiny for Python GitHub repository</a>.</p>
</div>
</div>
</div>
<section id="understanding-opentelemetry" class="level2">
<h2 class="anchored" data-anchor-id="understanding-opentelemetry">Understanding OpenTelemetry</h2>
<p>Before we dive into what’s new in Shiny, let’s talk about OpenTelemetry—a topic that might sound intimidating but is actually quite straightforward once we cover the basics.</p>
<section id="what-is-opentelemetry" class="level3">
<h3 class="anchored" data-anchor-id="what-is-opentelemetry">What is OpenTelemetry?</h3>
<p><a href="https://opentelemetry.io/"><strong>OpenTelemetry</strong></a> (aka OTel) describes itself as “high-quality, ubiquitous, and portable telemetry to enable effective observability”. In simpler terms, OpenTelemetry is a set of tools, APIs, and SDKs that help you collect and export telemetry data (like traces, logs, and metrics) from your applications. This data provides insights into how your applications are performing and behaving in real-world scenarios.</p>
<p>It captures three key types of data:</p>
<ol type="1">
<li><p><strong>Traces</strong>: These show the path of a request through your application. In a Shiny app, a trace might show how a user’s input triggered a series of reactive calculations, leading to updated outputs.</p></li>
<li><p><strong>Logs</strong>: These are detailed event records that capture what happened at specific moments.</p></li>
<li><p><strong>Metrics</strong>: These are numerical measurements over time, like how many users are connected or how long outputs take to render.</p></li>
</ol>
<p>These data types were standardized under the OpenTelemetry project, <a href="https://opentelemetry.io/community/marketing-guidelines/#i-opentelemetry-is-a-joint-effort">which is supported by a large community and many companies</a>. The goal is to provide a consistent way to collect and export observability data, making it easier to monitor and troubleshoot applications.</p>
</section>
<section id="the-opentelemetry-ecosystem" class="level3">
<h3 class="anchored" data-anchor-id="the-opentelemetry-ecosystem">The OpenTelemetry ecosystem</h3>
<p>OpenTelemetry is vendor-neutral, meaning you can send your telemetry data to various local backends like <a href="https://www.jaegertracing.io/">Jaeger</a>, <a href="https://zipkin.io/">Zipkin</a>, <a href="https://prometheus.io/">Prometheus</a>, or cloud-based services like <a href="https://grafana.com/products/cloud/">Grafana Cloud</a>, <a href="https://pydantic.dev/logfire">Logfire</a>, and <a href="https://langfuse.com/">Langfuse</a>. This flexibility means <a href="https://opentelemetry.io/community/marketing-guidelines/#iii-promote-awareness-of-otel-interoperability-and-modularization">you’re not locked into any particular monitoring solution</a>.</p>
<p>We’ve been using <a href="https://pydantic.dev/logfire">Logfire</a> internally at Posit to help develop OTel integration in many R packages and other applications. Throughout this post, you’ll see examples of OTel traces visualized in Logfire.</p>
<p>The image below shows an example trace in Logfire (left) from a Shiny app (right) that uses Generative AI to provide weather forecasts. The trace captures the entire user session, including reactive updates, model calls, and a tool invocation. We will explore this example in more detail later in the post.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-r-1.12/demo-app-and-logfire.png" class="img-fluid figure-img" alt="OpenTelemetry trace of chat app with weather tool"></p>
<figcaption>OpenTelemetry trace of chat app with weather tool</figcaption>
</figure>
</div>
</section>
</section>
<section id="opentelemetry-in-shiny" class="level2">
<h2 class="anchored" data-anchor-id="opentelemetry-in-shiny">OpenTelemetry in Shiny</h2>
<p>If you’ve ever wondered…</p>
<ul>
<li>“Why is my app slow for some users when hosted? Which reactive expressions are causing the slowdown?”</li>
<li>“How long does it take for my plot to render? Is it the data or the plotting code that is taking longer to calculate?”</li>
<li>“What’s the sequence of events leading up to when a user clicks <em>that</em> button?”</li>
<li>“How often are errors occurring in my app, and under what conditions?”</li>
</ul>
<p>Normally, we can attempt to answer these questions using <a href="https://rstudio.github.io/reactlog/"><code>{reactlog}</code></a> (a package to replay the recording of a reactive graph) or <a href="https://profvis.r-lib.org/"><code>{profvis}</code></a> (a package to profile R code in the main R process). However, both of these packages are not built for production use. These debugging tools are only to be used locally as they would be considered a memory leak in production.</p>
<p>OpenTelemetry allows us to record information <strong>at scale</strong> with minimal overhead, helping you answer previously impossible questions about your production environment. OpenTelemetry provides visibility into your app’s performance and behavior, helping you identify bottlenecks, debug issues, and optimize user experience—especially crucial when your app is deployed in production with real-world usage.</p>
<section id="adding-opentelemetry-integration" class="level3">
<h3 class="anchored" data-anchor-id="adding-opentelemetry-integration">Adding OpenTelemetry integration</h3>
<p>OTel support is automatically enabled in Shiny once <a href="https://otel.r-lib.org/"><code>{otel}</code></a> is able to record traces and logs.</p>
<p>To do this, let’s get started by installing the latest version of Shiny, <code>{otel}</code>, and <a href="https://otelsdk.r-lib.org"><code>{otelsdk}</code></a>:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1">pak<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pak</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shiny"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"otel"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"otelsdk"</span>))</span></code></pre></div>
<p>To enable OpenTelemetry tracing, you need set a few <a href="https://otelsdk.r-lib.org/reference/collecting.html">specific system environment variables</a> to describe where your recordings are being sent. In the example below, we set them in an <code>.Renviron</code> file to point to Logfire.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>.Renviron</strong></pre>
</div>
<div class="sourceCode" id="cb2" data-filename=".Renviron" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Enable OpenTelemetry by setting Collector environment variables</span></span>
<span id="cb2-2"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OTEL_TRACES_EXPORTER</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>http</span>
<span id="cb2-3"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OTEL_LOGS_EXPORTER</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>http</span>
<span id="cb2-4"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OTEL_LOG_LEVEL</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>debug</span>
<span id="cb2-5"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OTEL_METRICS_EXPORTER</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>http</span>
<span id="cb2-6"></span>
<span id="cb2-7"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OTEL_EXPORTER_OTLP_ENDPOINT</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://logfire-us.pydantic.dev"</span></span>
<span id="cb2-8"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OTEL_EXPORTER_OTLP_HEADERS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Authorization=&lt;your-write-token&gt;"</span></span></code></pre></div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Editing <code>.Renviron</code>
</div>
</div>
<div class="callout-body-container callout-body">
<p>You can edit your app-specific environment variables by calling <code>usethis::edit_r_environ(scope="project")</code> from within your Shiny app project directory.</p>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Verifying OTel setup
</div>
</div>
<div class="callout-body-container callout-body">
<p>You’ll know your setup is enabled if <a href="https://otel.r-lib.org/reference/is_tracing_enabled.html"><code>otel::is_tracing_enabled()</code></a> returns <code>TRUE</code>.</p>
</div>
</div>
</section>
<section id="opentelemetry-in-action" class="level3">
<h3 class="anchored" data-anchor-id="opentelemetry-in-action">OpenTelemetry in action</h3>
<p>Below is an example <code>{shinychat}</code> app with an <code>{ellmer}</code> tool to fetch realtime weather forecasts (via <code>{weathR}</code>, which uses <code>{httr2}</code>) for a given latitude and longitude. This simple (yet non-trivial) app helps us showcase what sort of information <code>{shiny}</code>, <code>{ellmer}</code>, and <code>{httr2}</code> can surface via OTel.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
OTel + GenAI
</div>
</div>
<div class="callout-body-container callout-body">
<p>Gaining timing insights into applications that leverage <a href="https://aws.amazon.com/what-is/generative-ai/">Generative AI</a> (GenAI) is critical to improving user experience. Without OpenTelemetry, if a user stated an app was slow, we would not be able to accurately determine if the slowness was due to the AI model request time, AI model streaming time, tool execution time, or even followup reactive calculations in Shiny.</p>
</div>
</div>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>app.R</strong></pre>
</div>
<div class="sourceCode" id="cb3" data-filename="app.R" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shiny)</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create tool that grabs the weather forecast (free) for a given lat/lon</span></span>
<span id="cb3-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Inspired from: https://posit-dev.github.io/shinychat/r/articles/tool-ui.html</span></span>
<span id="cb3-5">get_weather_forecast_basic <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb3-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(lat, lon) {</span>
<span id="cb3-7">    weathR<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">point_tomorrow</span>(lat, lon, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">short =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb3-8">  },</span>
<span id="cb3-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"get_weather_forecast"</span>,</span>
<span id="cb3-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Get the weather forecast for a location."</span>,</span>
<span id="cb3-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">arguments =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb3-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lat =</span> ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Latitude"</span>),</span>
<span id="cb3-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lon =</span> ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Longitude"</span>)</span>
<span id="cb3-14">  )</span>
<span id="cb3-15">)</span>
<span id="cb3-16"></span>
<span id="cb3-17">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> bslib<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fillable</span>(</span>
<span id="cb3-18">  shinychat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_mod_ui</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"100%"</span>)</span>
<span id="cb3-19">)</span>
<span id="cb3-20">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb3-21">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set up client within `server` to not _share_ the client for all sessions</span></span>
<span id="cb3-22">  client <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_claude</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Be terse."</span>)</span>
<span id="cb3-23">  client<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(get_weather_forecast_basic)</span>
<span id="cb3-24"></span>
<span id="cb3-25">  chat_server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> shinychat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_mod_server</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, client, session)</span>
<span id="cb3-26"></span>
<span id="cb3-27">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set chat placeholder on app init</span></span>
<span id="cb3-28">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb3-29">    chat_server<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">update_user_input</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"What is the weather in Atlanta, GA?"</span>)</span>
<span id="cb3-30">  }, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"set-default-input"</span>)</span>
<span id="cb3-31">}</span>
<span id="cb3-32"></span>
<span id="cb3-33"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
</div>
<p>You’ll notice that the <code>app.R</code> has no OpenTelemetry specific code.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-r-1.12/demo-app.png" alt="Chat app with weather tool" height="500" class="figure-img"></p>
<figcaption>Chat app with weather tool</figcaption>
</figure>
</div>
<p>Here’s an example trace from Logfire showing a user session interacting with the chat app and the weather tool:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-r-1.12/demo-logfire.png" class="img-fluid figure-img" alt="OpenTelemetry trace of chat app with weather tool"></p>
<figcaption>OpenTelemetry trace of chat app with weather tool</figcaption>
</figure>
</div>
<p>The traces above recorded a single user session where the user asked for the weather in Atlanta, GA and then closed the app. The trace shows:</p>
<ul>
<li>The Shiny session lifecycle, including <code>session_start</code> and <code>session_end</code></li>
<li>Many <code>{shinychat}</code> <code>chat</code> module spans for handling user input and messages</li>
<li>Reactive updates triggered by changes in the <code>session</code>’s input</li>
<li>An <code>ExtendedTask</code> span for the computation of the AI agent response</li>
<li>2x <code>chat claude</code> spans representing calls to the AI agent model</li>
<li>A single <code>get_weather_forecast</code> tool call being executed, including the HTTP requests made by <code>{httr2}</code> to fetch the weather data</li>
</ul>
<p>The gap between this span’s length and its parent’s length is how long the results took to stream back to the user or make a decision. For the overall user experience, the total time taken from input to output is represented by the <code>ExtendedTask</code> span, 9.5 seconds in this case. Only a half of a second was spent in the tool call (something <em>we as app authors</em> could possibly optimize). The remaining 9 seconds was spent in the model response generation and streaming.</p>
<p>Notice how the spans are nested, showing the relationship between user actions, required reactive calculations, and external API calls. This level of detail helps you understand exactly how your app is performing in production and where any bottlenecks or issues may arise.</p>
<div class="callout callout-style-default callout-note no-icon callout-titled">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-6-contents" aria-controls="callout-6" aria-expanded="true" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon no-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Packages used in demo
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-6" class="callout-6-contents callout-collapse collapse show">
<div class="callout-body-container callout-body">
<p>The Shiny app above currently requires the development version of <code>{ellmer}</code> to record OpenTelemetry traces. More R packages (mentioned later in this post) will contain native OpenTelemetry support in their upcoming releases.</p>
</div>
</div>
</div>
</section>
<section id="what-can-shiny-record" class="level3">
<h3 class="anchored" data-anchor-id="what-can-shiny-record">What can Shiny record?</h3>
<p>Shiny automatically creates OpenTelemetry spans for:</p>
<ul>
<li><strong>Session lifecycle</strong>: When sessions start and end, including HTTP request details</li>
<li><strong>Reactive updates</strong>: The entire cascade of reactive calculations triggered by an input change or a new output to be rendered</li>
<li><strong>Reactive expressions</strong>: Individual <code>reactive()</code>, <code>observe()</code>, and <code>output</code> calculations; <code>debounce()</code> and <code>throttle()</code> value updates; <code>reactiveFileReader()</code> and <code>reactivePoll()</code> updates</li>
<li><strong>Extended tasks</strong>: Long-running background computations</li>
</ul>
<p>Additionally, Shiny logs events for:</p>
<ul>
<li>Fatal or unhandled errors (with optional error message sanitization)</li>
<li>When a <code>reactiveVal()</code> is set and</li>
<li>When a <code>reactiveValues()</code> value is set</li>
</ul>
<p>Every span and log entry provided by Shiny includes the session ID, making it easy to filter and analyze data for specific user sessions.</p>
<p>At the time of this post, no metrics (numerical measurements over time) are automatically recorded for Shiny. However, <a href="https://plumber2.posit.co/"><code>{plumber2}</code></a> (via <a href="https://reqres.data-imaginist.com/"><code>{reqres}</code></a>) has added OTel metrics in their latest release: a counter of the number of active requests and histograms for request durations and request/response body sizes.</p>
<p>For more detailed information on configuring and using OpenTelemetry within R, check out the <a href="https://otel.r-lib.org/"><code>{otel}</code> package documentation</a> and how to set up record collection with <a href="https://otelsdk.r-lib.org/reference/collecting.html"><code>{otelsdk}</code></a>.</p>
</section>
<section id="fine-grained-control" class="level3">
<h3 class="anchored" data-anchor-id="fine-grained-control">Fine-grained control</h3>
<p>Automatic tracing is perfect to get started, but you may want more control over what gets traced. Shiny v1.12 gives you that flexibility through the <code>shiny.otel.collect</code> option (or <code>SHINY_OTEL_COLLECT</code> environment variable). You can set this to control the level of tracing detail:</p>
<ul>
<li><code>"none"</code> - No Shiny OpenTelemetry tracing</li>
<li><code>"session"</code> - Track session start and end</li>
<li><code>"reactive_update"</code> - Track reactive updates (includes <code>"session"</code> tracing)</li>
<li><code>"reactivity"</code> - Trace all reactive expressions (includes <code>"reactive_update"</code> tracing)</li>
<li><code>"all"</code> - Everything (currently equivalent to “reactivity”)</li>
</ul>
<p>For example:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Only trace session lifecycle, not every reactive calculation</span></span>
<span id="cb4-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">options</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shiny.otel.collect =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"session"</span>)</span></code></pre></div>
<p>This is especially useful in production environments where you want observability without overwhelming your telemetry backend with data.</p>
<p>You can also temporarily override the <code>shiny.otel.collect</code> option within a specific block of code using the <code>withOtelCollect()</code> or <code>localOtelCollect()</code> functions. This is helpful when you want to exclude certain parts of your app from being traced. For example, to avoid tracing Shiny reactive expressions within a block:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Do not trace _Shiny_ reactive expressions within this block</span></span>
<span id="cb5-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># All other otel spans/logs will still be recorded</span></span>
<span id="cb5-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">withOtelCollect</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>, {</span>
<span id="cb5-4">  expensive_calculation <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reactive</span>({</span>
<span id="cb5-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start a custom span for this calculation</span></span>
<span id="cb5-6">    otel<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">start_local_active_span</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my custom span"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tracer =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my_tracer"</span>)</span>
<span id="cb5-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.sleep</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Simulate a long calculation</span></span>
<span id="cb5-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rnorm</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e6</span>)</span>
<span id="cb5-9">  })</span>
<span id="cb5-10">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>plot <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderPlot</span>({</span>
<span id="cb5-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">hist</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">expensive_calculation</span>())</span>
<span id="cb5-12">  })</span>
<span id="cb5-13">})</span></code></pre></div>
</section>
</section>
<section id="in-summary" class="level2">
<h2 class="anchored" data-anchor-id="in-summary">In summary</h2>
<p>Whether you’re developing apps locally or managing production deployments, OpenTelemetry support in Shiny now gives you visibility into your applications:</p>
<ul>
<li><strong>Developers</strong> can debug complex reactive flows</li>
<li><strong>Performance engineers</strong> can identify and eliminate bottlenecks</li>
<li><strong>Data scientists</strong> can understand how users interact with their applications</li>
</ul>
<p>We’re excited to see how the community uses this powerful new capability. Try it out, and let us know what you think!</p>
<p>While OpenTelemetry support is the star of this release, Shiny v1.12 includes several other nice improvements. For a complete list of changes, check out the NEWS entries for <a href="../../../r/reference/shiny/1.12.0/upgrade.html">Shiny v1.12.0</a> and <a href="../../../r/reference/shiny/1.12.1/upgrade.html">Shiny v1.12.1</a>.</p>
</section>
<section id="learn-more" class="level2">
<h2 class="anchored" data-anchor-id="learn-more">Learn more</h2>
<p>For more details on OpenTelemetry in Shiny, check out our <a href="../../../r/articles/improve/opentelemetry/">dedicated article on OpenTelemetry + Shiny</a> article.</p>
</section>
<section id="whats-next" class="level2">
<h2 class="anchored" data-anchor-id="whats-next">What’s next?</h2>
<p>We have big plans for OpenTelemetry in Shiny and the broader R ecosystem. Existing and upcoming enhancements include:</p>
<ul>
<li><a href="https://mirai.r-lib.org/"><code>{mirai}</code></a> v2.5.0</li>
<li><a href="https://rstudio.github.io/promises/"><code>{promises}</code></a> v1.5.0</li>
<li><a href="https://plumber2.posit.co/"><code>{plumber2}</code></a> (via <a href="https://fiery.data-imaginist.com/"><code>{fiery}</code></a> v1.4.0, <a href="https://reqres.data-imaginist.com/"><code>{reqres}</code></a> v1.1.0, and <a href="https://routr.data-imaginist.com/"><code>{routr}</code></a> v1.1.0)</li>
<li><a href="https://httr2.r-lib.org/"><code>{httr2}</code></a> v1.2.2</li>
<li><a href="https://yihui.org/knitr/"><code>{knitr}</code></a> (<code>yihui/knitr</code>)</li>
<li><a href="https://ellmer.tidyverse.org/"><code>{ellmer}</code></a> (<a href="https://github.com/tidyverse/ellmer/pull/526"><code>tidyverse/ellmer#526</code></a>)</li>
<li><a href="https://dbi.r-dbi.org/"><code>{DBI}</code></a> (<a href="https://github.com/r-dbi/DBI/pull/551"><code>r-dbi/dbi#551</code></a>)</li>
<li><a href="https://testthat.r-lib.org/"><code>{testthat}</code></a> (<a href="https://github.com/r-lib/testthat/pull/2282"><code>r-lib/testthat#2282</code></a>)</li>
</ul>
<p>In addition, the Shiny Team’s focus will be shifting to add OpenTelemetry integration to Shiny for Python. Be sure to be on the lookout for the future announcement!</p>
</section>
<section id="acknowledgements" class="level2">
<h2 class="anchored" data-anchor-id="acknowledgements">Acknowledgements</h2>
<p>A big thank you to all [the folks/everyone] who [helped make this release happen/contributed to this release]:</p>
<p><a href="https://github.com/andriygm">@andriygm</a>, <a href="https://github.com/BajczA475">@BajczA475</a>, <a href="https://github.com/bedantaguru">@bedantaguru</a>, <a href="https://github.com/billdenney">@billdenney</a>, <a href="https://github.com/chendaniely">@chendaniely</a>, <a href="https://github.com/Copilot">@Copilot</a>, <a href="https://github.com/cpsievert">@cpsievert</a>, <a href="https://github.com/csgillespie">@csgillespie</a>, <a href="https://github.com/deadbytesus">@deadbytesus</a>, <a href="https://github.com/dempsey-CMAR">@dempsey-CMAR</a>, <a href="https://github.com/dleopold">@dleopold</a>, <a href="https://github.com/federiva">@federiva</a>, <a href="https://github.com/federivaFirebird">@federivaFirebird</a>, <a href="https://github.com/fzenoni">@fzenoni</a>, <a href="https://github.com/gadenbuie">@gadenbuie</a>, <a href="https://github.com/gsmolinski">@gsmolinski</a>, <a href="https://github.com/ismirsehregal">@ismirsehregal</a>, <a href="https://github.com/janxkoci">@janxkoci</a>, <a href="https://github.com/JohnADawson">@JohnADawson</a>, <a href="https://github.com/karangattu">@karangattu</a>, <a href="https://github.com/marcozanotti">@marcozanotti</a>, <a href="https://github.com/Mkranj">@Mkranj</a>, <a href="https://github.com/mm225022">@mm225022</a>, <a href="https://github.com/mmuurr">@mmuurr</a>, <a href="https://github.com/nicholasdavies">@nicholasdavies</a>, <a href="https://github.com/r2evans">@r2evans</a>, <a href="https://github.com/schloerke">@schloerke</a>, <a href="https://github.com/shikokuchuo">@shikokuchuo</a>, <a href="https://github.com/simon-smart88">@simon-smart88</a>, <a href="https://github.com/vedhav">@vedhav</a>, and <a href="https://github.com/wch">@wch</a>.</p>
<hr>
<p><em>Have questions or feedback? Join the conversation on the <a href="https://community.rstudio.com/c/shiny">Shiny Community forum</a> or <a href="https://github.com/rstudio/shiny">GitHub</a>.</em></p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-r-1.12/</guid>
  <pubDate>Wed, 10 Dec 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-r-1.12/feature.png" medium="image" type="image/png" height="80" width="144"/>
</item>
<item>
  <title>Shiny Talks from posit::conf(2025)</title>
  <link>https://shiny.posit.co/blog/posts/conf-2025-shinytalks/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        


<p>Videos from posit::conf(2025) are now available. To make it easier to explore how people are building with Shiny across R and Python, we’ve put together a curated playlist of the Shiny-focused talks from the conference. These sessions highlight practical apps, new tooling, design patterns, AI workflows, and real-world deployments built with Shiny.</p>
<section id="shiny-talks-playlist" class="level2 page-columns page-full">
<h2 data-anchor-id="shiny-talks-playlist">Shiny Talks Playlist:</h2>
<div class="column-page-inset video-container mb-5 mt-3">
<iframe width="560" height="315" src="https://www.youtube.com/embed/Kh8xDym8sTg?si=trOED7T3iArnY7Kj" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="">
</iframe>
</div>
<p>Talks included in the playlist, broken up into a few categories for easier browsing, are as follows:</p>
</section>
<section id="shiny-ai-intelligent-automation" class="level2">
<h2 data-anchor-id="shiny-ai-intelligent-automation">Shiny + AI &amp; Intelligent Automation</h2>
<table class="caption-top table">
<colgroup>
<col style="width: 30%">
<col style="width: 40%">
<col style="width: 30%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: right;">Speakers</th>
<th style="text-align: center;">Title</th>
<th style="text-align: left;">Link</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;"><strong>Winston Chang (Shiny Team)</strong></td>
<td style="text-align: center;">Web applications with Shiny and React (and AI)</td>
<td style="text-align: left;"><a href="https://youtu.be/Kh8xDym8sTg"><img src="https://img.youtube.com/vi/Kh8xDym8sTg/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="even">
<td style="text-align: right;"><strong>Karan Gathani (Shiny Team)</strong></td>
<td style="text-align: center;">Old Apps, New Tricks: How AI can write Automated Tests for Shiny</td>
<td style="text-align: left;"><a href="https://youtu.be/gxcAadq0Bmk"><img src="https://img.youtube.com/vi/gxcAadq0Bmk/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="odd">
<td style="text-align: right;"><strong>Regis A. James</strong></td>
<td style="text-align: center;">AskRADS: An AI Recommendation Agent for Maximizing Shiny Development</td>
<td style="text-align: left;"><a href="https://youtu.be/v3CCoq7j9Tk"><img src="https://img.youtube.com/vi/v3CCoq7j9Tk/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
</tbody>
</table>
</section>
<section id="shiny-architecture-performance-data" class="level2">
<h2 data-anchor-id="shiny-architecture-performance-data">Shiny Architecture, Performance &amp; Data</h2>
<table class="caption-top table">
<colgroup>
<col style="width: 30%">
<col style="width: 40%">
<col style="width: 30%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: right;">Speakers</th>
<th style="text-align: center;">Title</th>
<th style="text-align: left;">Link</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;"><strong>Alex Chisholm</strong></td>
<td style="text-align: center;">Keeping Data Alive: Persistent Storage Options for Shiny</td>
<td style="text-align: left;"><a href="https://youtu.be/Pe-XTCKUzV8"><img src="https://img.youtube.com/vi/Pe-XTCKUzV8/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="even">
<td style="text-align: right;"><strong>Melissa Albino Hegeman</strong></td>
<td style="text-align: center;">Get your ducks in a row… faster Shiny apps with DuckDB</td>
<td style="text-align: left;"><a href="https://www.youtube.com/watch?v=2sLJSosz1OY"><img src="https://i.ytimg.com/vi/2sLJSosz1OY/hqdefault.jpg?sqp=-oaymwEjCNACELwBSFryq4qpAxUIARUAAAAAGAElAADIQj0AgKJDeAE=&amp;rs=AOn4CLBZKR5FG40MaA-0o4SMJg3cKepLdQ" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="odd">
<td style="text-align: right;"><strong>Barret Schloerke (Shiny Team)</strong></td>
<td style="text-align: center;">Observability at scale: Monitoring Shiny Applications</td>
<td style="text-align: left;"><a href="https://youtu.be/zwxhFKRlQLs"><img src="https://img.youtube.com/vi/zwxhFKRlQLs/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
</tbody>
</table>
</section>
<section id="shiny-ux-ui-design-accessibility" class="level2">
<h2 data-anchor-id="shiny-ux-ui-design-accessibility">Shiny UX, UI Design &amp; Accessibility</h2>
<table class="caption-top table">
<colgroup>
<col style="width: 30%">
<col style="width: 40%">
<col style="width: 30%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: right;">Speakers</th>
<th style="text-align: center;">Title</th>
<th style="text-align: left;">Link</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;"><strong>Cameron Race; Sarah Wong-Brown</strong></td>
<td style="text-align: center;">shinyGovStyle – accessible government design in Shiny</td>
<td style="text-align: left;"><a href="https://youtu.be/33doZkPSUqY"><img src="https://img.youtube.com/vi/33doZkPSUqY/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="even">
<td style="text-align: right;"><strong>Casey Aguilar-Gervase; Maya Gans</strong></td>
<td style="text-align: center;">Design of Everyday Shiny Apps</td>
<td style="text-align: left;"><a href="https://youtu.be/RQod46DSkiA"><img src="https://img.youtube.com/vi/RQod46DSkiA/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="odd">
<td style="text-align: right;"><strong>Jeremy Winget, PhD</strong></td>
<td style="text-align: center;">Death by Dropdown? Engineer Insightful Shiny Apps</td>
<td style="text-align: left;"><a href="https://youtu.be/QvMnUZLQ-oo"><img src="https://img.youtube.com/vi/QvMnUZLQ-oo/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="even">
<td style="text-align: right;"><strong>Kim Schouten</strong></td>
<td style="text-align: center;">Modular, layout-as-code approach for customizable Shiny dashboards</td>
<td style="text-align: left;"><a href="https://youtu.be/HJs2HVltpcs"><img src="https://img.youtube.com/vi/HJs2HVltpcs/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
</tbody>
</table>
</section>
<section id="shiny-extensions-packages-developer-tooling" class="level2 text-left">
<h2 class="text-left" data-anchor-id="shiny-extensions-packages-developer-tooling">Shiny Extensions, Packages &amp; Developer Tooling</h2>
<table class="caption-top table">
<colgroup>
<col style="width: 30%">
<col style="width: 40%">
<col style="width: 30%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: right;">Speakers</th>
<th style="text-align: center;">Title</th>
<th style="text-align: left;">Link</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;"><strong>Eric Nantz</strong></td>
<td style="text-align: center;">shinystate: Launching collaboration and session state</td>
<td style="text-align: left;"><a href="https://youtu.be/69bnkXD3e6w"><img src="https://img.youtube.com/vi/69bnkXD3e6w/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="even">
<td style="text-align: right;"><strong>James Wade</strong></td>
<td style="text-align: center;">shinyEventLogger — logging events in Shiny apps</td>
<td style="text-align: left;"><a href="https://youtu.be/smnrmTtoiOM"><img src="https://img.youtube.com/vi/smnrmTtoiOM/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
</tbody>
</table>
</section>
<section id="applied-shiny-case-studies" class="level2">
<h2 data-anchor-id="applied-shiny-case-studies">Applied Shiny Case Studies</h2>
<table class="caption-top table">
<colgroup>
<col style="width: 30%">
<col style="width: 40%">
<col style="width: 30%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: right;">Speakers</th>
<th style="text-align: center;">Title</th>
<th style="text-align: left;">Link</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;"><strong>Marcus Beck</strong></td>
<td style="text-align: center;">Shiny for ecological data workflows</td>
<td style="text-align: left;"><a href="https://youtu.be/Cnk3770AzuA"><img src="https://img.youtube.com/vi/Cnk3770AzuA/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
<tr class="even">
<td style="text-align: right;"><strong>Hugo Fitipaldi</strong></td>
<td style="text-align: center;">Building a Real-Time COVID-19 Surveillance System with Shiny</td>
<td style="text-align: left;"><a href="https://youtu.be/3-UsetFXFlk"><img src="https://img.youtube.com/vi/3-UsetFXFlk/maxresdefault.jpg" class="img-fluid" alt="thumb"></a></td>
</tr>
</tbody>
</table>
</section>
<section id="shiny-workshops-from-positconf2025" class="level2 pt-4">
<h2 class="pt-4" data-anchor-id="shiny-workshops-from-positconf2025">Shiny Workshops from posit::conf(2025)</h2>
<p>Full workshop materials are now available, including slides, exercises, and code for all Shiny workshops.</p>
<h3 class="d-inline me-2">
LLMs + Shiny Workshop
</h3>
/
<p class="d-inline ms-2">
Joe Cheng &amp; Garrick Aden-Buie
</p>
<div class="mt-3 mb-5">
<p><a href="https://posit-conf-2025.github.io/llm/" class="btn btn-outline-dark btn-sm my-3 d-inline me-3">LLM Course Materials</a><a href="https://github.com/posit-conf-2025/llm"><img src="https://shiny.posit.co/images/github.svg" style="width:16px;height:auto;display:inline;"></a></p>
</div>
<h3 class="d-inline me-2">
Shiny for Python Workshop
</h3>
/
<p class="d-inline ms-2">
Daniel Chen
</p>
<div class="mt-3 mb-5">
<p><a href="https://posit-conf-2025.github.io/shiny-py/" class="btn btn-outline-dark btn-sm my-3 d-inline me-3">Shiny for R Materials</a><a href="https://github.com/posit-conf-2025/shiny-py"><img src="https://shiny.posit.co/images/github.svg" style="width:16px;height:auto;display:inline;"></a></p>
</div>
<h3 class="d-inline me-2">
Shiny for R Workshop
</h3>
/
<p class="d-inline ms-2">
Colin Rundel
</p>
<div class="mt-3 mb-5">
<p><a href="https://posit-conf-2025.github.io/shiny-r/" class="btn btn-outline-dark btn-sm my-3 d-inline me-3">Shiny for Python Materials</a><a href="https://github.com/posit-conf-2025/shiny-r/"><img src="https://shiny.posit.co/images/github.svg" style="width:16px;height:auto;display:inline;"></a></p>
</div>
</section>
<section id="closing" class="level2">
<h2 data-anchor-id="closing">Closing</h2>
<p>We hope you enjoy this roundup of Shiny talks from posit::conf(2025). The Shiny community continues to explore new patterns—AI-assisted development, real-time analytics, performance tooling, design systems, and more. We hope to see you next year at posit::conf—and maybe even see <em>your</em> Shiny work up on stage.</p>
<style>
  td, th {text-align: left !important;}
  /*tr:last-of-type td {border-bottom: 0px !important;}
  table {border-bottom: 1px solid transparent !important;}*/
</style>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/conf-2025-shinytalks/</guid>
  <pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/conf-2025-shinytalks/conf-recordings-banner.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>Tool Calling UI in shinychat</title>
  <dc:creator>Garrick Aden-Buie</dc:creator>
  <dc:creator>Carson Sievert</dc:creator>
  <dc:creator>Barret Schloerke</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shinychat-tool-ui/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        


<style>
.highlight-line {
  font-weight: bold;
}
body:not(.modal-open) div.sourceCode pre code.has-line-highlights > span:not(.highlight-line) {
  opacity: 0.6;
}
body:not(.modal-open) div.sourceCode:hover pre code.has-line-highlights > span:not(.highlight-line) {
  opacity: 0.8;
}
.card-header {
  --bs-card-cap-bg: transparent;
}
.shiny-tool-card {
  margin-bottom: 1rem !important;
}
.code-copy-button > .bi::after {
  display: none;
}
</style>
<iframe src="feature/index.html" class="ratio ratio-16x9 rounded" style="width:133%;border:0;height: 450px;transform: scale(0.75);transform-origin: 0 0;margin-bottom: -100px;">
  <!-- Fallback content for non‑iframe environments -->
  <div class="column-body-outset blog-image-container pb-5">
      <img
          src="feature.png"
          alt="shinychat has tool calling UI in v0.2.0 (Python) and v0.3.0 (R)!"
          style="width: 100%; height: auto;">
  </div>
</iframe>
<p>We’re jazzed to announce that <a href="https://posit-dev.github.io/shinychat">shinychat</a> now includes rich UI for tool calls! shinychat makes it easy to build LLM-powered chat interfaces in Shiny apps, and with tool calling UI, your users can see which tools are being executed and their outcomes. This feature is available in <a href="https://posit-dev.github.io/shinychat/r">shinychat for R</a> (v0.3.0) and <a href="https://posit-dev.github.io/shinychat/py/">shinychat for Python</a> (v0.2.0 or later).</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false">Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<div class="sourceCode" id="cb1" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shinychat"</span>)</span></code></pre></div>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<div class="sourceCode" id="cb2" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install shinychat</span></code></pre></div>
</div>
</div>
</div>
<p>This release brings tool call displays that work with <a href="https://ellmer.tidyverse.org">ellmer</a> (R) and <a href="https://github.com/posit-dev/chatlas">chatlas</a> (Python). When the LLM calls a tool, shinychat automatically displays the request and result in a collapsible card interface.</p>
<p>In this post we’ll cover the new Tool calling UI features, how to set them up in your apps, and ways to customize the display. We’ll also highlight some chat bookmarking support and other improvements in shinychat for R v0.3.0. As always, you can find the full list of changes in the <a href="https://posit-dev.github.io/shinychat/r/news/index.html#shinychat-030">R release notes</a> and <a href="https://github.com/posit-dev/shinychat/blob/main/pkg-py/CHANGELOG.md">Python release notes</a>.</p>
<section id="tool-calling-ui" class="level2">
<h2 data-anchor-id="tool-calling-ui">Tool calling UI</h2>
<p>Tool calling lets you extend an LLM’s capabilities by giving it access to functions you define. When you provide a tool to the LLM, you’re telling it “here’s a function you can call if you need it.” The key thing to understand is that the tool runs on <em>your machine</em> (or wherever your Shiny app is running) — the LLM doesn’t directly run the tool itself. Instead, it asks <em>you</em> to run the function and return the result.</p>
<p>Both ellmer and chatlas make it easy to define tools and register them with your chat client<sup>1</sup>, and they also handle the back-and-forth of tool calls by receiving requests from the LLM, executing the tool, and sending the results back. This means you can focus on what you do best: writing code to solve problems.</p>
<p>Any problem you can solve with a function can become a tool for an LLM! You can give the LLM access to live data, APIs, databases, or any other resources your app can reach.</p>
<div class="callout callout-style-default callout-tip callout-titled" title="btw: A complete toolkit for R">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
btw: A complete toolkit for R
</div>
</div>
<div class="callout-body-container callout-body">
<p>If you’re working in R, <a href="https://posit-dev.github.io/btw">btw</a> is a complete toolkit to help LLMs work better with R. Whether you’re copy-pasting to ChatGPT, chatting with an AI assistant in your IDE, or building LLM-powered apps with shinychat, btw makes it easy to give LLMs the context they need.</p>
<p>And, most importantly, btw provides a full suite of tools for gathering context from R sessions, including tools to: read help pages and vignettes, describe data frames, search for packages on CRAN, read web pages, and more.</p>
<p>Learn more at <a href="https://posit-dev.github.io/btw">posit-dev.github.io/btw</a>!</p>
</div>
</div>
<p>When the LLM decides to call a tool, shinychat displays the request and result in the chat interface. Users can see which tools are being invoked, what arguments are being passed, and what data is being returned. The tool display is designed to be customizable, so shinychat developers can customize the appearance and display of tool calls to best serve their users.</p>
<section id="basic-tool-display" class="level3">
<h3 data-anchor-id="basic-tool-display">Basic tool display</h3>
<p>Let’s start by creating a simple weather forecasting tool that fetches a weather data (in the United States) for a given latitude and longitude.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-2-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-1" aria-controls="tabset-2-1" aria-selected="true">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-2-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-2" aria-controls="tabset-2-2" aria-selected="false">Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-2-1" class="tab-pane active" aria-labelledby="tabset-2-1-tab">
<div class="cell">
<div class="sourceCode cell-code" id="cb3" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode numberSource r number-lines code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shinychat)</span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(weathR)</span>
<span id="cb3-4"></span>
<span id="cb3-5">get_weather_forecast <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb3-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(lat, lon) {</span>
<span id="cb3-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">point_tomorrow</span>(lat, lon, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">short =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb3-8">  },</span>
<span id="cb3-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"get_weather_forecast"</span>,</span>
<span id="cb3-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Get the weather forecast for a location."</span>,</span>
<span id="cb3-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">arguments =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb3-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lat =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Latitude"</span>),</span>
<span id="cb3-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lon =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Longitude"</span>)</span>
<span id="cb3-14">  )</span>
<span id="cb3-15">)</span>
<span id="cb3-16"></span>
<span id="cb3-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with your chat client</span></span>
<span id="cb3-18">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"openai/gpt-4.1-nano"</span>)</span>
<span id="cb3-19">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(get_weather_forecast)</span></code></pre></div>
</div>
</div>
<div id="tabset-2-2" class="tab-pane" aria-labelledby="tabset-2-2-tab">
<div class="sourceCode" id="cb4" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatOpenAI</span>
<span id="cb4-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> requests</span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> get_weather_forecast(lat: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, lon: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">dict</span>:</span>
<span id="cb4-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Get the weather forecast for a location."""</span></span>
<span id="cb4-6">    lat_lng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"latitude=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>lat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;longitude=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>lon<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb4-7">    url <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"https://api.open-meteo.com/v1/forecast?</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>lat_lng<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;current=temperature_2m,wind_speed_10m"</span></span>
<span id="cb4-8">    response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> requests.get(url)</span>
<span id="cb4-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> response.json()[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"current"</span>]</span>
<span id="cb4-10"></span>
<span id="cb4-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with your chat client</span></span>
<span id="cb4-12">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatOpenAI(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt-4.1-nano"</span>)</span>
<span id="cb4-13">chat.register_tool(get_weather_forecast)</span></code></pre></div>
</div>
</div>
</div>
<p>With this tool registered, when you ask a weather-related question, the LLM might decide to call the <code>get_weather_forecast()</code> tool to get the latest weather.</p>
<p>In a chat conversation in your R console with ellmer, this might look like the following.</p>
<div class="sourceCode" id="cb5" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Will I need an umbrella for my walk to the T?"</span>)</span>
<span id="cb5-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ◯ [tool call] get_weather_forecast(lat = 42.3515, lon = -71.0552)</span></span>
<span id="cb5-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ● #&gt; [{"time":"2025-11-20 16:00:00 EST","temp":42,"dewpoint":0,"humidity":67,"p_rain":1,"wi…</span></span>
<span id="cb5-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;</span></span>
<span id="cb5-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Based on the weather forecast, there is a chance of rain around 4 to 5 PM,</span></span>
<span id="cb5-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; with mostly cloudy to partly sunny skies. It seems there might be some rain</span></span>
<span id="cb5-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; during this time, so carrying an umbrella could be a good idea if you plan</span></span>
<span id="cb5-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; to go out around that time. Otherwise, the weather looks relatively clear</span></span>
<span id="cb5-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; in the evening.</span></span></code></pre></div>
<p>Notice that I didn’t provide many context clues, but the model correctly guessed that I’m walking to the MBTA in Boston, MA and picked <a href="https://www.openstreetmap.org/?mlat=42.35150&amp;mlon=-71.05520#map=16/42.35150/-71.05520&amp;layers=P">the latitude and longitude for Boston’s South Station</a>.</p>
<p>In shinychat, when the LLM calls the tool, shinychat automatically displays the tool request in a collapsed card:</p>
<div class="cell">
<div class="cell-output-display">
<shiny-tool-request request-id="tool_call_001" tool-name="get_weather_forecast" arguments="{&quot;lat&quot;:42.3515,&quot;lon&quot;:-71.0552}"></shiny-tool-request>
</div>
</div>
<p>Expanding the card shows the arguments passed to the tool. When the tool completes, shinychat replaces the request with a card containing the result:</p>
<div class="cell">
<div class="cell-output-display">
<shiny-tool-result request-id="tool_call_002" tool-name="get_weather_forecast" request-call="get_weather_forecast(lat = 42.3515, lon = -71.0552)" status="success" show-request="" value="[
  {
    &quot;time&quot;: &quot;2025-11-21 11:00:00 EST&quot;,
    &quot;temp&quot;: 43,
    &quot;dewpoint&quot;: -0.5556,
    &quot;humidity&quot;: 62,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 12:00:00 EST&quot;,
    &quot;temp&quot;: 45,
    &quot;dewpoint&quot;: -0.5556,
    &quot;humidity&quot;: 58,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 10,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Partly Sunny&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 13:00:00 EST&quot;,
    &quot;temp&quot;: 47,
    &quot;dewpoint&quot;: 0,
    &quot;humidity&quot;: 56,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 13,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 14:00:00 EST&quot;,
    &quot;temp&quot;: 47,
    &quot;dewpoint&quot;: 1.1111,
    &quot;humidity&quot;: 60,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 12,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 15:00:00 EST&quot;,
    &quot;temp&quot;: 47,
    &quot;dewpoint&quot;: 1.6667,
    &quot;humidity&quot;: 63,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 10,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 16:00:00 EST&quot;,
    &quot;temp&quot;: 46,
    &quot;dewpoint&quot;: 2.7778,
    &quot;humidity&quot;: 71,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 17:00:00 EST&quot;,
    &quot;temp&quot;: 46,
    &quot;dewpoint&quot;: 3.3333,
    &quot;humidity&quot;: 73,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 18:00:00 EST&quot;,
    &quot;temp&quot;: 44,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 82,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 19:00:00 EST&quot;,
    &quot;temp&quot;: 44,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 82,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 20:00:00 EST&quot;,
    &quot;temp&quot;: 42,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 89,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 21:00:00 EST&quot;,
    &quot;temp&quot;: 43,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 86,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 22:00:00 EST&quot;,
    &quot;temp&quot;: 42,
    &quot;dewpoint&quot;: 4.4444,
    &quot;humidity&quot;: 93,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;W&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 23:00:00 EST&quot;,
    &quot;temp&quot;: 42,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 89,
    &quot;p_rain&quot;: 2,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;W&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  }
]" value-type="code"></shiny-tool-result>
</div>
</div>
<p>If the tool throws an error, the error is captured and the error message is shown to the LLM. Typically this happens when the model makes a mistake in calling the tool and often the error message is instructive.</p>
<p>shinychat updates the card to show the error message:</p>
<div class="cell">
<div class="cell-output-display">
<shiny-tool-result request-id="tool_call_001c" tool-name="get_weather_forecast" request-call="get_weather_forecast(lat = 42.3515, lon = -71.0552)" status="error" show-request="" value="object of type 'closure' is not subsettable" value-type="code"></shiny-tool-result>
</div>
</div>
</section>
<section id="setting-up-streaming" class="level3">
<h3 data-anchor-id="setting-up-streaming">Setting up streaming</h3>
<p>To enable tool UI in your apps, you need to ensure that tool requests and results are streamed to shinychat:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-3-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-1" aria-controls="tabset-3-1" aria-selected="true">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-3-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-2" aria-controls="tabset-3-2" aria-selected="false">Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-3-1" class="tab-pane active" aria-labelledby="tabset-3-1-tab">
<p>You don’t need to do anything if you’re using <code>chat_app()</code> or the chat module via <code>chat_mod_ui()</code> and <code>chat_mod_server()</code>; tool UI is enabled automatically.</p>
<p>If you’re using <code>chat_ui()</code> with <code>chat_append()</code>, set <code>stream = "content"</code> when calling <code>$stream_async()</code>:</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb6" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb6-2">  client <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"openai/gpt-4.1-nano"</span>)</span>
<span id="cb6-3">  client<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(get_weather_forecast)</span>
<span id="cb6-4"></span>
<span id="cb6-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observeEvent</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>chat_user_input, {</span>
<span id="cb6-6">    stream <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> client<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stream_async</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>chat_user_input, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stream =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"content"</span>)</span>
<span id="cb6-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_append</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, stream)</span>
<span id="cb6-8">  })</span>
<span id="cb6-9">}</span></code></pre></div>
</div>
</div>
<div id="tabset-3-2" class="tab-pane" aria-labelledby="tabset-3-2-tab">
<p>In Python with Shiny Express, use <code>content="all"</code> when calling <code>stream_async()</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>app.py</strong></pre>
</div>
<div class="sourceCode" id="cb7" data-filename="app.py" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatOpenAI</span>
<span id="cb7-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ui</span>
<span id="cb7-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shinychat.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Chat</span>
<span id="cb7-4"></span>
<span id="cb7-5">client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatOpenAI(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt-4.1-nano"</span>)</span>
<span id="cb7-6">client.register_tool(get_weather_forecast)</span>
<span id="cb7-7"></span>
<span id="cb7-8">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Chat(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>)</span>
<span id="cb7-9">chat.ui()</span>
<span id="cb7-10"></span>
<span id="cb7-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@chat.on_user_submit</span></span>
<span id="cb7-12"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> handle_user_input(user_input: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb7-13">    response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> client.stream_async(user_input, content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"all"</span>)</span>
<span id="cb7-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.append_message_stream(response)</span></code></pre></div>
</div>
<p>For Shiny Core mode:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>app.py</strong></pre>
</div>
<div class="sourceCode" id="cb8" data-filename="app.py" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatOpenAI</span>
<span id="cb8-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> App, ui</span>
<span id="cb8-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shinychat <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Chat</span>
<span id="cb8-4"></span>
<span id="cb8-5">client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatOpenAI(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt-4.1-nano"</span>)</span>
<span id="cb8-6">client.register_tool(get_weather_forecast)</span>
<span id="cb8-7"></span>
<span id="cb8-8">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fluid(</span>
<span id="cb8-9">    Chat(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>).ui()</span>
<span id="cb8-10">)</span>
<span id="cb8-11"></span>
<span id="cb8-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb8-13">    chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Chat(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>)</span>
<span id="cb8-14"></span>
<span id="cb8-15">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@chat.on_user_submit</span></span>
<span id="cb8-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> handle_user_input(user_input: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb8-17">        response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> client.stream_async(user_input, content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"all"</span>)</span>
<span id="cb8-18">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.append_message_stream(response)</span>
<span id="cb8-19"></span>
<span id="cb8-20">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
</div>
</div>
</div>
</div>
</section>
<section id="customizing-tool-title-and-icon" class="level3">
<h3 data-anchor-id="customizing-tool-title-and-icon">Customizing tool title and icon</h3>
<p>You can enhance the visual presentation of tool requests and results by adding custom titles and icons to your tools. This helps users quickly identify which tools are being called.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-4-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-1" aria-controls="tabset-4-1" aria-selected="true">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-4-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-2" aria-controls="tabset-4-2" aria-selected="false">Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-4-1" class="tab-pane active" aria-labelledby="tabset-4-1-tab">
<p>Use <code>tool_annotations()</code> to add a title and icon:</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb9" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode numberSource r number-lines code-with-copy"><code class="sourceCode r"><span id="cb9-1">get_weather_forecast <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb9-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(lat, lon) {</span>
<span id="cb9-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">point_tomorrow</span>(lat, lon, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">short =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb9-4">  },</span>
<span id="cb9-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"get_weather_forecast"</span>,</span>
<span id="cb9-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Get the weather forecast for a location."</span>,</span>
<span id="cb9-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">arguments =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb9-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lat =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Latitude"</span>),</span>
<span id="cb9-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lon =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Longitude"</span>)</span>
<span id="cb9-10">  ),</span>
<span id="cb9-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">annotations =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool_annotations</span>(</span>
<span id="cb9-12">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Weather Forecast"</span>,</span>
<span id="cb9-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">icon =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cloud-sun"</span>)</span>
<span id="cb9-14">  )</span>
<span id="cb9-15">)</span></code></pre></div>
</div>
</div>
<div id="tabset-4-2" class="tab-pane" aria-labelledby="tabset-4-2-tab">
<p>With chatlas, you can customize the tool display in two ways:</p>
<ol type="1">
<li><p>Use the <code>._display</code> attribute to customize the tool display:</p>
<div class="sourceCode" id="cb10" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> faicons</span>
<span id="cb10-2"></span>
<span id="cb10-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> get_weather_forecast(lat: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, lon: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">dict</span>:</span>
<span id="cb10-4">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Get the weather forecast for a location."""</span></span>
<span id="cb10-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ... implementation ...</span></span>
<span id="cb10-6"></span>
<span id="cb10-7">get_weather_forecast._display <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {</span>
<span id="cb10-8">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Weather Forecast"</span>,</span>
<span id="cb10-9">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"icon"</span>: faicons.icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cloud-sun"</span>)</span>
<span id="cb10-10">}</span></code></pre></div>
<p>This approach sets the title and icon for all calls to this tool, so it’s ideal for predefined tools or tools that are bundled in a Python module or package.</p></li>
<li><p>Set the tool annotations at registration time:</p>
<div class="sourceCode" id="cb11" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1">chat.register_tool(</span>
<span id="cb11-2">    get_weather_forecast,</span>
<span id="cb11-3">    annotations<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb11-4">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Weather Forecast"</span>,</span>
<span id="cb11-5">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"icon"</span>: faicons.icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cloud-sun"</span>)</span>
<span id="cb11-6">    }</span>
<span id="cb11-7">)</span></code></pre></div>
<p>This approach allows you to customize the display for a specific chat client or application without modifying the tool function itself.</p></li>
</ol>
</div>
</div>
</div>
<p>Now the tool card shows your custom title and icon:</p>
<div class="cell">
<div class="cell-output-display">
<shiny-tool-result request-id="tool_call_004" tool-name="get_weather_forecast" request-call="get_weather_forecast(lat = 42.3515, lon = -71.0552)" status="success" tool-title="Weather Forecast" icon="<svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 16 16&quot; class=&quot;bi bi-cloud-sun &quot; style=&quot;height:1em;width:1em;fill:currentColor;vertical-align:-0.125em;&quot; aria-hidden=&quot;true&quot; role=&quot;img&quot; ><path d=&quot;M7 8a3.5 3.5 0 0 1 3.5 3.555.5.5 0 0 0 .624.492A1.503 1.503 0 0 1 13 13.5a1.5 1.5 0 0 1-1.5 1.5H3a2 2 0 1 1 .1-3.998.5.5 0 0 0 .51-.375A3.502 3.502 0 0 1 7 8zm4.473 3a4.5 4.5 0 0 0-8.72-.99A3 3 0 0 0 3 16h8.5a2.5 2.5 0 0 0 0-5h-.027z&quot;></path>
<path d=&quot;M10.5 1.5a.5.5 0 0 0-1 0v1a.5.5 0 0 0 1 0v-1zm3.743 1.964a.5.5 0 1 0-.707-.707l-.708.707a.5.5 0 0 0 .708.708l.707-.708zm-7.779-.707a.5.5 0 0 0-.707.707l.707.708a.5.5 0 1 0 .708-.708l-.708-.707zm1.734 3.374a2 2 0 1 1 3.296 2.198c.199.281.372.582.516.898a3 3 0 1 0-4.84-3.225c.352.011.696.055 1.028.129zm4.484 4.074c.6.215 1.125.59 1.522 1.072a.5.5 0 0 0 .039-.742l-.707-.707a.5.5 0 0 0-.854.377zM14.5 6.5a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1h-1z&quot;></path></svg>" show-request="" value="[
  {
    &quot;time&quot;: &quot;2025-11-21 11:00:00 EST&quot;,
    &quot;temp&quot;: 43,
    &quot;dewpoint&quot;: -0.5556,
    &quot;humidity&quot;: 62,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 12:00:00 EST&quot;,
    &quot;temp&quot;: 45,
    &quot;dewpoint&quot;: -0.5556,
    &quot;humidity&quot;: 58,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 10,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Partly Sunny&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 13:00:00 EST&quot;,
    &quot;temp&quot;: 47,
    &quot;dewpoint&quot;: 0,
    &quot;humidity&quot;: 56,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 13,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 14:00:00 EST&quot;,
    &quot;temp&quot;: 47,
    &quot;dewpoint&quot;: 1.1111,
    &quot;humidity&quot;: 60,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 12,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 15:00:00 EST&quot;,
    &quot;temp&quot;: 47,
    &quot;dewpoint&quot;: 1.6667,
    &quot;humidity&quot;: 63,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 10,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 16:00:00 EST&quot;,
    &quot;temp&quot;: 46,
    &quot;dewpoint&quot;: 2.7778,
    &quot;humidity&quot;: 71,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 17:00:00 EST&quot;,
    &quot;temp&quot;: 46,
    &quot;dewpoint&quot;: 3.3333,
    &quot;humidity&quot;: 73,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 18:00:00 EST&quot;,
    &quot;temp&quot;: 44,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 82,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 19:00:00 EST&quot;,
    &quot;temp&quot;: 44,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 82,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 20:00:00 EST&quot;,
    &quot;temp&quot;: 42,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 89,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 21:00:00 EST&quot;,
    &quot;temp&quot;: 43,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 86,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;SW&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 22:00:00 EST&quot;,
    &quot;temp&quot;: 42,
    &quot;dewpoint&quot;: 4.4444,
    &quot;humidity&quot;: 93,
    &quot;p_rain&quot;: 1,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;W&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  },
  {
    &quot;time&quot;: &quot;2025-11-21 23:00:00 EST&quot;,
    &quot;temp&quot;: 42,
    &quot;dewpoint&quot;: 3.8889,
    &quot;humidity&quot;: 89,
    &quot;p_rain&quot;: 2,
    &quot;wind_speed&quot;: 9,
    &quot;wind_dir&quot;: &quot;W&quot;,
    &quot;skies&quot;: &quot;Mostly Cloudy&quot;,
    &quot;geometry&quot;: {
      &quot;type&quot;: &quot;Point&quot;,
      &quot;coordinates&quot;: [-71.0589, 42.3601]
    }
  }
]" value-type="code"></shiny-tool-result>
</div>
</div>
</section>
<section id="custom-display-content" class="level3">
<h3 data-anchor-id="custom-display-content">Custom display content</h3>
<p>By default, shinychat shows the raw tool result value as a code block. But often you’ll want to present data to users in a more polished format—like a formatted table or a summary.</p>
<p>You can customize the display by returning alternative content:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-5-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-1" aria-controls="tabset-5-1" aria-selected="true">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-5-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-2" aria-controls="tabset-5-2" aria-selected="false">Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-5-1" class="tab-pane active" aria-labelledby="tabset-5-1-tab">
<p>Return a <code>ContentToolResult</code> with <code>extra$display</code> containing alternative content:</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb12" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode numberSource r number-lines code-with-copy"><code class="sourceCode r"><span id="cb12-1">get_weather_forecast <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb12-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(lat, lon, location_name) {</span>
<span id="cb12-3">    forecast_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">point_tomorrow</span>(lat, lon, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">short =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb12-4">    forecast_table <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_raw_html</span>(gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gt</span>(forecast_data))</span>
<span id="cb12-5"></span>
<span id="cb12-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ContentToolResult</span>(</span>
<span id="cb12-7">      forecast_data,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># This is what the LLM sees</span></span>
<span id="cb12-8">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">extra =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb12-9">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">display =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb12-10">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">html =</span> forecast_table,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># This is what users see</span></span>
<span id="cb12-11">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Weather Forecast for"</span>, location_name)</span>
<span id="cb12-12">        )</span>
<span id="cb12-13">      )</span>
<span id="cb12-14">    )</span>
<span id="cb12-15">  },</span>
<span id="cb12-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"get_weather_forecast"</span>,</span>
<span id="cb12-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Get the weather forecast for a location."</span>,</span>
<span id="cb12-18">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">arguments =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb12-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lat =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Latitude"</span>),</span>
<span id="cb12-20">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lon =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Longitude"</span>),</span>
<span id="cb12-21">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">location_name =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Name of the location"</span>)</span>
<span id="cb12-22">  ),</span>
<span id="cb12-23">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">annotations =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool_annotations</span>(</span>
<span id="cb12-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Weather Forecast"</span>,</span>
<span id="cb12-25">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">icon =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cloud-sun"</span>)</span>
<span id="cb12-26">  )</span>
<span id="cb12-27">)</span></code></pre></div>
</div>
</div>
<div id="tabset-5-2" class="tab-pane" aria-labelledby="tabset-5-2-tab">
<p>Return a <code>ToolResult</code> with display options:</p>
<div class="sourceCode" id="cb13" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ToolResult</span>
<span id="cb13-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> pandas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> pd</span>
<span id="cb13-3"></span>
<span id="cb13-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> get_weather_forecast(lat: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, lon: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, location_name: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb13-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Get the weather forecast for a location."""</span></span>
<span id="cb13-6">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get forecast data</span></span>
<span id="cb13-7">    data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fetch_weather_data(lat, lon)</span>
<span id="cb13-8"></span>
<span id="cb13-9">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create a DataFrame for the LLM</span></span>
<span id="cb13-10">    forecast_df <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.DataFrame(data)</span>
<span id="cb13-11"></span>
<span id="cb13-12">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create HTML table for users</span></span>
<span id="cb13-13">    forecast_table <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> forecast_df.to_html(index<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb13-14"></span>
<span id="cb13-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ToolResult(</span>
<span id="cb13-16">        value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>forecast_df.to_dict(),  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># LLM sees this</span></span>
<span id="cb13-17">        display<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb13-18">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"html"</span>: forecast_table,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Users see this</span></span>
<span id="cb13-19">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"title"</span>: <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Weather Forecast for </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>location_name<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb13-20">        }</span>
<span id="cb13-21">    )</span></code></pre></div>
</div>
</div>
</div>
<p>The <code>display</code> options support three content types (in order of preference):</p>
<ol type="1">
<li><strong><code>html</code></strong>: HTML content from packages like <code>{gt}</code>, <code>{reactable}</code>, or <code>{htmlwidgets}</code> (R), or Pandas/HTML strings (Python)</li>
<li><strong><code>markdown</code></strong>: Markdown text that’s automatically rendered</li>
<li><strong><code>text</code></strong>: Plain text without code formatting</li>
</ol>
<p>Here’s what a formatted table looks like in the tool result:</p>
<div class="cell">
<div class="cell-output-display">
<shiny-tool-result request-id="tool_call_007" tool-name="get_weather_forecast" request-call="get_weather_forecast(lat = 42.3515, lon = -71.0552, location_name = &quot;South Station in Boston, MA&quot;)" status="success" tool-title="Weather Forecast for South Station in Boston, MA" icon="<svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 16 16&quot; class=&quot;bi bi-cloud-sun &quot; style=&quot;height:1em;width:1em;fill:currentColor;vertical-align:-0.125em;&quot; aria-hidden=&quot;true&quot; role=&quot;img&quot; ><path d=&quot;M7 8a3.5 3.5 0 0 1 3.5 3.555.5.5 0 0 0 .624.492A1.503 1.503 0 0 1 13 13.5a1.5 1.5 0 0 1-1.5 1.5H3a2 2 0 1 1 .1-3.998.5.5 0 0 0 .51-.375A3.502 3.502 0 0 1 7 8zm4.473 3a4.5 4.5 0 0 0-8.72-.99A3 3 0 0 0 3 16h8.5a2.5 2.5 0 0 0 0-5h-.027z&quot;></path>
<path d=&quot;M10.5 1.5a.5.5 0 0 0-1 0v1a.5.5 0 0 0 1 0v-1zm3.743 1.964a.5.5 0 1 0-.707-.707l-.708.707a.5.5 0 0 0 .708.708l.707-.708zm-7.779-.707a.5.5 0 0 0-.707.707l.707.708a.5.5 0 1 0 .708-.708l-.708-.707zm1.734 3.374a2 2 0 1 1 3.296 2.198c.199.281.372.582.516.898a3 3 0 1 0-4.84-3.225c.352.011.696.055 1.028.129zm4.484 4.074c.6.215 1.125.59 1.522 1.072a.5.5 0 0 0 .039-.742l-.707-.707a.5.5 0 0 0-.854.377zM14.5 6.5a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1h-1z&quot;></path></svg>" show-request="" value="<div id=&quot;pkzpybuiyj&quot; style=&quot;padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;&quot;>
  
  <table class=&quot;gt_table&quot; data-quarto-disable-processing=&quot;false&quot; data-quarto-bootstrap=&quot;false&quot; style=&quot;-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; display: table; border-collapse: collapse; line-height: normal; margin-left: auto; margin-right: auto; color: #333333; font-size: 16px; font-weight: normal; font-style: normal; background-color: #FFFFFF; width: auto; border-top-style: solid; border-top-width: 2px; border-top-color: #A8A8A8; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #A8A8A8; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3;&quot; bgcolor=&quot;#FFFFFF&quot;>
  <thead style=&quot;border-style: none;&quot;>
    <tr class=&quot;gt_col_headings&quot; style=&quot;border-style: none; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3;&quot;>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_left&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;time&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: left;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;left&quot;>time</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_right&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;temp&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;right&quot;>temp</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_right&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;dewpoint&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;right&quot;>dewpoint</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_right&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;humidity&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;right&quot;>humidity</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_right&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;p_rain&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;right&quot;>p_rain</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_right&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;wind_speed&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;right&quot;>wind_speed</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_left&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;wind_dir&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: left;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;left&quot;>wind_dir</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_left&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;skies&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: left;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;left&quot;>skies</th>
      <th class=&quot;gt_col_heading gt_columns_bottom_border gt_center&quot; rowspan=&quot;1&quot; colspan=&quot;1&quot; scope=&quot;col&quot; id=&quot;geometry&quot; style=&quot;border-style: none; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; text-align: center;&quot; bgcolor=&quot;#FFFFFF&quot; valign=&quot;bottom&quot; align=&quot;center&quot;>geometry</th>
    </tr>
  </thead>
  <tbody class=&quot;gt_table_body&quot; style=&quot;border-style: none; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3;&quot;>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 11:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>42</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>-0.5555556</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>65</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>10</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 12:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>43</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>-0.5555556</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>62</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>12</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Partly Sunny</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 13:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>45</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>0.0000000</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>60</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>13</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 14:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>47</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1.1111111</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>60</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>12</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 15:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>47</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1.6666667</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>63</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>10</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 16:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>47</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>2.2222222</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>65</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 17:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>48</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>3.3333333</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>68</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 18:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>47</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>3.8888889</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>74</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 19:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>47</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>3.8888889</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>74</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 20:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>47</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>3.8888889</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>74</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 21:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>46</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>4.4444444</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>79</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>SW</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 22:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>45</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>3.8888889</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>79</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>1</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>W</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
    <tr style=&quot;border-style: none;&quot;><td headers=&quot;time&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>2025-11-21 23:00:00 EST</td>
<td headers=&quot;temp&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>45</td>
<td headers=&quot;dewpoint&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>4.4444444</td>
<td headers=&quot;humidity&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>82</td>
<td headers=&quot;p_rain&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>2</td>
<td headers=&quot;wind_speed&quot; class=&quot;gt_row gt_right&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: right; font-variant-numeric: tabular-nums;&quot; valign=&quot;middle&quot; align=&quot;right&quot;>9</td>
<td headers=&quot;wind_dir&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>W</td>
<td headers=&quot;skies&quot; class=&quot;gt_row gt_left&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: left;&quot; valign=&quot;middle&quot; align=&quot;left&quot;>Mostly Cloudy</td>
<td headers=&quot;geometry&quot; class=&quot;gt_row gt_center&quot; style=&quot;border-style: none; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; text-align: center;&quot; valign=&quot;middle&quot; align=&quot;center&quot;>c(-71.0552, 42.3515)</td></tr>
  </tbody>
  
</table>
</div>" value-type="html"></shiny-tool-result>
</div>
</div>
</section>
<section id="additional-display-options" class="level3">
<h3 data-anchor-id="additional-display-options">Additional display options</h3>
<p>You can control how tool results are presented using additional display options:</p>
<ul>
<li><code>show_request = FALSE</code>: Hide the tool call details when they’re obvious from the display</li>
<li><code>open = TRUE</code>: Expand the result panel by default (useful for rich content like maps or charts)</li>
<li><code>title</code> and <code>icon</code>: Override the tool’s default title and icon for this specific result</li>
</ul>
<p>Another helpful feature is to include an <code>_intent</code> argument in your tool definition. When present in the tool arguments, shinychat shows the <code>_intent</code> value in the tool card header, helping users understand why the LLM is calling the tool.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-6-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-1" aria-controls="tabset-6-1" aria-selected="true">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-6-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-2" aria-controls="tabset-6-2" aria-selected="false">Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-6-1" class="tab-pane active" aria-labelledby="tabset-6-1-tab">
<div class="cell">
<div class="sourceCode cell-code" id="cb14" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1">tool_with_intent <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb14-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">_intent</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>) {</span>
<span id="cb14-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runif</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb14-4">  },</span>
<span id="cb14-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"random_number"</span>,</span>
<span id="cb14-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Generate a random number."</span>,</span>
<span id="cb14-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">arguments =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb14-8">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">_intent</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb14-9">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Explain why you're generating this number"</span></span>
<span id="cb14-10">    )</span>
<span id="cb14-11">  )</span>
<span id="cb14-12">)</span></code></pre></div>
</div>
</div>
<div id="tabset-6-2" class="tab-pane" aria-labelledby="tabset-6-2-tab">
<div class="sourceCode" id="cb15" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> random_number(_intent: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>:</span>
<span id="cb15-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Generate a random number.</span></span>
<span id="cb15-3"></span>
<span id="cb15-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Args:</span></span>
<span id="cb15-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        _intent: Explain why you're generating this number</span></span>
<span id="cb15-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb15-7">    <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> random</span>
<span id="cb15-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> random.random()</span></code></pre></div>
</div>
</div>
</div>
<p>Notice that the tool function itself doesn’t actually use the <code>_intent</code> argument, but its presence allows shinychat to give the user additional context about the tool call.</p>
</section>
</section>
<section id="bookmarking-support" class="level2">
<h2 data-anchor-id="bookmarking-support">Bookmarking support</h2>
<p>When a Shiny app reloads, the app returns to its initial state, unless the URL includes <a href="https://shiny.posit.co/r/articles/build/bookmarking-state/">bookmarked state</a>.<sup>2</sup> Automatically updating the URL to include a bookmark of the chat state is a great way to help users return to their work if they accidentally refresh the page or unexpectedly lose their connection.</p>
<p>Both shinychat for R and Python provide helper functions that make it easy to restore conversations with bookmarks. This means users can refresh the page or share a URL and pick up right where they left off.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-7-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-1" aria-controls="tabset-7-1" aria-selected="true">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-7-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-2" aria-controls="tabset-7-2" aria-selected="false">Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-7-1" class="tab-pane active" aria-labelledby="tabset-7-1-tab">
<p>In R, the <code>chat_restore()</code> function restores the message history from the bookmark when the app starts up <em>and</em> ensures that the chat client state is automatically bookmarked on user input and assistant responses.</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb16" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb16-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shiny)</span>
<span id="cb16-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shinychat)</span>
<span id="cb16-3"></span>
<span id="cb16-4">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(request) {</span>
<span id="cb16-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fillable</span>(</span>
<span id="cb16-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_ui</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>)</span>
<span id="cb16-7">  )</span>
<span id="cb16-8">}</span>
<span id="cb16-9"></span>
<span id="cb16-10">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb16-11">  chat_client <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_openai</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt-4o-mini"</span>)</span>
<span id="cb16-12"></span>
<span id="cb16-13">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Automatically save chat state on user input and responses</span></span>
<span id="cb16-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_restore</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, chat_client)</span>
<span id="cb16-15"></span>
<span id="cb16-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observeEvent</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>chat_user_input, {</span>
<span id="cb16-17">    stream <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> chat_client<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stream_async</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>chat_user_input)</span>
<span id="cb16-18">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_append</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, stream)</span>
<span id="cb16-19">  })</span>
<span id="cb16-20">}</span>
<span id="cb16-21"></span>
<span id="cb16-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Enable URL-based bookmarking</span></span>
<span id="cb16-23"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">enableBookmarking =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"url"</span>)</span></code></pre></div>
</div>
<p><code>enableBookmarking = "url"</code> stores the chat state in encoded data in the query string of the app’s URL. Because browsers have native limitations on the size of a URL, you should use <code>enableBookmarking = "server"</code> to store state server-side without URL size limitations for chatbots expected to have large conversation histories.</p>
<p>And if you’re using <code>chat_app()</code> for quick prototypes, bookmarking is already enabled automatically.</p>
</div>
<div id="tabset-7-2" class="tab-pane" aria-labelledby="tabset-7-2-tab">
<p>In Python, the <code>.enable_bookmarking()</code> method handles the where, when, and how of bookmarking chat state.</p>
<section id="express-mode" class="level3">
<h3 data-anchor-id="express-mode">Express mode</h3>
<div class="sourceCode" id="cb17" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatOllama</span>
<span id="cb17-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ui</span>
<span id="cb17-3"></span>
<span id="cb17-4">chat_client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatOllama(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"llama3.2"</span>)</span>
<span id="cb17-5"></span>
<span id="cb17-6">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.Chat(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>)</span>
<span id="cb17-7">chat.ui(messages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Welcome!"</span>])</span>
<span id="cb17-8"></span>
<span id="cb17-9">chat.enable_bookmarking(</span>
<span id="cb17-10">    chat_client,</span>
<span id="cb17-11">    bookmark_store<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"url"</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># or "server"</span></span>
<span id="cb17-12">    bookmark_on<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"response"</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># or None</span></span>
<span id="cb17-13">)</span></code></pre></div>
</section>
<section id="core-mode" class="level3">
<h3 data-anchor-id="core-mode">Core mode</h3>
<div class="sourceCode" id="cb18" data-code-line-numbers="" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb18-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatOllama</span>
<span id="cb18-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ui, App</span>
<span id="cb18-3"></span>
<span id="cb18-4">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fixed(</span>
<span id="cb18-5">    ui.chat_ui(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, messages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Welcome!"</span>])</span>
<span id="cb18-6">)</span>
<span id="cb18-7"></span>
<span id="cb18-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>):</span>
<span id="cb18-9">    chat_client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatOllama(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"llama3.2"</span>)</span>
<span id="cb18-10">    chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.Chat(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>)</span>
<span id="cb18-11"></span>
<span id="cb18-12">    chat.enable_bookmarking(</span>
<span id="cb18-13">        chat_client,</span>
<span id="cb18-14">        bookmark_on<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"response"</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># or None</span></span>
<span id="cb18-15">    )</span>
<span id="cb18-16"></span>
<span id="cb18-17">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server, bookmark_store<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"url"</span>)</span></code></pre></div>
</section>
<section id="configuration-options" class="level3">
<h3 data-anchor-id="configuration-options">Configuration options</h3>
<p>The <code>.enable_bookmarking()</code> method handles three aspects of bookmarking:</p>
<ol type="1">
<li><strong>Where</strong> (<code>bookmark_store</code>)
<ul>
<li><code>"url"</code>: Store the state in the URL.</li>
<li><code>"server"</code>: Store the state on the server. Consider this over <code>"url"</code> if you want to support a large amount of state, or have other bookmark state that can’t be serialized to JSON.</li>
</ul></li>
<li><strong>When</strong> (<code>bookmark_on</code>)
<ul>
<li><code>"response"</code>: Triggers a bookmark when an <code>"assistant"</code> response is appended.</li>
<li><code>None</code>: Don’t trigger a bookmark automatically. This assumes you’ll be triggering bookmarks through other means (e.g., a button).</li>
</ul></li>
<li><strong>How</strong> is handled automatically by registering the relevant <code>on_bookmark</code> and <code>on_restore</code> callbacks.</li>
</ol>
<p>When <code>.enable_bookmarking()</code> triggers a bookmark for you, it’ll also update the URL query string to include the bookmark state. This way, when the user unexpectedly loses connection, they can load the current URL to restore the chat state, or go back to the original URL to start over.</p>
</section>
</div>
</div>
</div>
</section>
<section id="other-improvements-in-shinychat-for-r" class="level2">
<h2 data-anchor-id="other-improvements-in-shinychat-for-r">Other improvements in shinychat for R</h2>
<p>Beyond tool calling UI and bookmarking support, shinychat for R v0.3.0 includes several other enhancements.</p>
<section id="better-programmatic-control" class="level3">
<h3 data-anchor-id="better-programmatic-control">Better programmatic control</h3>
<p><code>chat_mod_server()</code> now returns a set of reactive values and functions for controlling the chat interface:</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb19" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb19-1">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb19-2">  chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_mod_server</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, ellmer<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_openai</span>())</span>
<span id="cb19-3"></span>
<span id="cb19-4">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># React to user input</span></span>
<span id="cb19-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb19-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">last_input</span>())</span>
<span id="cb19-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"User said:"</span>, chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">last_input</span>()))</span>
<span id="cb19-8">  })</span>
<span id="cb19-9"></span>
<span id="cb19-10">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># React to assistant responses</span></span>
<span id="cb19-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb19-12">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">last_turn</span>())</span>
<span id="cb19-13">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Assistant completed response"</span>)</span>
<span id="cb19-14">  })</span>
<span id="cb19-15"></span>
<span id="cb19-16">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Programmatically control the chat</span></span>
<span id="cb19-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observeEvent</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>suggest_question, {</span>
<span id="cb19-18">    chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">update_user_input</span>(</span>
<span id="cb19-19">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"What's the weather like today?"</span>,</span>
<span id="cb19-20">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">submit =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Automatically submit</span></span>
<span id="cb19-21">    )</span>
<span id="cb19-22">  })</span>
<span id="cb19-23"></span>
<span id="cb19-24">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observeEvent</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>reset, {</span>
<span id="cb19-25">    chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">clear</span>()  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Clear history and UI</span></span>
<span id="cb19-26">  })</span>
<span id="cb19-27">}</span></code></pre></div>
</div>
<p>The returned list includes:</p>
<ul>
<li><strong><code>last_input</code></strong> and <strong><code>last_turn</code></strong> reactives for monitoring chat state</li>
<li><strong><code>update_user_input()</code></strong> for programmatically setting or submitting user input—great for suggested prompts or guided conversations</li>
<li><strong><code>append()</code></strong> for adding messages to the chat UI</li>
<li><strong><code>clear()</code></strong> for resetting the chat, with options to control how the client history is handled</li>
<li><strong><code>client</code></strong> for direct access to the ellmer chat client</li>
</ul>
<p>There’s also a standalone <code>update_chat_user_input()</code> function if you’re using <code>chat_ui()</code> directly, which supports updating the placeholder text and moving focus to the input.</p>
</section>
<section id="custom-assistant-icons" class="level3">
<h3 data-anchor-id="custom-assistant-icons">Custom assistant icons</h3>
<p>You can now customize the icon shown next to assistant messages to better match your application’s branding or to distinguish between different assistants:</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb20" data-source-line-numbers="nil" data-code-line-numbers="nil" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb20-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bsicons)</span>
<span id="cb20-2"></span>
<span id="cb20-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set a custom icon for a specific response</span></span>
<span id="cb20-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_append</span>(</span>
<span id="cb20-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>,</span>
<span id="cb20-6">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here's some helpful information!"</span>,</span>
<span id="cb20-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">icon =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightbulb"</span>)</span>
<span id="cb20-8">)</span>
<span id="cb20-9"></span>
<span id="cb20-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Or set a default icon for all assistant messages</span></span>
<span id="cb20-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_ui</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chat"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">icon_assistant =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robot"</span>))</span></code></pre></div>
</div>
<p>This is especially useful when building multi-agent applications where different assistants might have different personalities or roles.</p>
</section>
<section id="safer-external-links" class="level3">
<h3 data-anchor-id="safer-external-links">Safer external links</h3>
<p>External links in chat messages now open in a new tab with a confirmation dialog. This prevents users from accidentally navigating away from the chat session and losing their conversation. This is particularly helpful when LLMs include links in their responses, for example when shinychat is used in combination with Retrieval Augmented Generation via <a href="https://ragnar.tidyverse.org">ragnar</a>.</p>
</section>
</section>
<section id="learn-more" class="level2">
<h2 data-anchor-id="learn-more">Learn more</h2>
<p>The tool calling UI opens up exciting possibilities for building transparent, user-friendly AI applications. Whether you’re fetching data, running calculations, or integrating with external services, users can now see exactly what’s happening.</p>
<p>To dive deeper:</p>
<ul>
<li>Read the <a href="https://posit-dev.github.io/shinychat/r/articles/tool-ui.html">tool calling UI article</a> for comprehensive examples in R</li>
<li>Explore tool calling with <a href="https://ellmer.tidyverse.org/articles/tool-calling.html">ellmer</a> (R) or <a href="https://posit-dev.github.io/chatlas/tool-calling/displays.html">chatlas</a> (Python)</li>
</ul>
</section>
<section id="acknowledgements" class="level2">
<h2 data-anchor-id="acknowledgements">Acknowledgements</h2>
<p>A huge thank you to everyone who contributed to this release with bug reports, feature requests, and code contributions:</p>
<p><a href="https://github.com/bianchenhao">@bianchenhao</a>, <a href="https://github.com/cboettig">@cboettig</a>, <a href="https://github.com/chendaniely">@chendaniely</a>, <a href="https://github.com/cpsievert">@cpsievert</a>, <a href="https://github.com/DavZim">@DavZim</a>, <a href="https://github.com/DeepanshKhurana">@DeepanshKhurana</a>, <a href="https://github.com/DivadNojnarg">@DivadNojnarg</a>, <a href="https://github.com/gadenbuie">@gadenbuie</a>, <a href="https://github.com/iainwallacebms">@iainwallacebms</a>, <a href="https://github.com/janlimbeck">@janlimbeck</a>, <a href="https://github.com/jcheng5">@jcheng5</a>, <a href="https://github.com/jimrothstein">@jimrothstein</a>, <a href="https://github.com/karangattu">@karangattu</a>, <a href="https://github.com/ManuelSpinola">@ManuelSpinola</a>, <a href="https://github.com/MohoWu">@MohoWu</a>, <a href="https://github.com/nissinbo">@nissinbo</a>, <a href="https://github.com/noamanemobidata">@noamanemobidata</a>, <a href="https://github.com/parmsam">@parmsam</a>, <a href="https://github.com/PaulC91">@PaulC91</a>, <a href="https://github.com/rkennedy01">@rkennedy01</a>, <a href="https://github.com/schloerke">@schloerke</a>, <a href="https://github.com/selesnow">@selesnow</a>, <a href="https://github.com/simonpcouch">@simonpcouch</a>, <a href="https://github.com/skaltman">@skaltman</a>, <a href="https://github.com/stefanlinner">@stefanlinner</a>, <a href="https://github.com/t-kalinowski">@t-kalinowski</a>, <a href="https://github.com/thendrix-trlm">@thendrix-trlm</a>, <a href="https://github.com/wch">@wch</a>, <a href="https://github.com/wlandau">@wlandau</a>, and <a href="https://github.com/Yousuf28">@Yousuf28</a>.</p>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>See the <a href="https://ellmer.tidyverse.org/articles/tool-calling.html">ellmer tool calling documentation</a> for R and the <a href="https://posit-dev.github.io/chatlas/tool-calling/how-it-works.html">chatlas tool calling documentation</a> for Python for more details on defining and registering tools.↩︎</p></li>
<li id="fn2"><p>This can be especially frustrating behavior since hosted apps, by default, will close an idle session after a certain (<a href="https://docs.posit.co/shinyapps.io/guide/applications/#advanced-settings">configurable</a>) amount of time.↩︎</p></li>
</ol>
</section></div> ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shinychat-tool-ui/</guid>
  <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shinychat-tool-ui/feature.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>promises v1.5.0</title>
  <dc:creator>Barret Schloerke</dc:creator>
  <link>https://shiny.posit.co/blog/posts/promises-1.5.0/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/promises-1.5.0/feature.png" alt="promises v1.5.0" style="width: 100%; height: auto;">
    </div>
    


<div class="lead">
<p>We’re excited to announce the release of <a href="https://rstudio.github.io/promises/">promises</a> v1.5.0. The <a href="https://rstudio.github.io/promises/">promises</a> package provides fundamental abstractions for asynchronous programming in R using JavaScript-style promises, enabling a single R process to orchestrate multiple concurrent tasks.</p>
</div>
<section id="getting-started" class="level1">
<h1>Getting Started</h1>
<p><a href="https://rstudio.github.io/promises/">promises</a> provides fundamental abstractions for asynchronous programming in R using JavaScript-style promises. It enables a single R process to orchestrate multiple tasks in the background while remaining responsive to other operations. Key features include:</p>
<ul>
<li><strong>Promise chaining</strong> with <a href="https://rstudio.github.io/promises/reference/then.html"><code>then()</code></a>, <a href="https://rstudio.github.io/promises/reference/then.html"><code>catch()</code></a>, and <a href="https://rstudio.github.io/promises/reference/then.html"><code>finally()</code></a> for handling asynchronous results</li>
<li><strong>Combinators</strong> like <a href="https://rstudio.github.io/promises/reference/promise_all.html"><code>promise_all()</code></a> and <a href="https://rstudio.github.io/promises/reference/promise_all.html"><code>promise_race()</code></a> for coordinating multiple async operations</li>
<li><strong>Promise domains</strong> for managing execution context across asynchronous boundaries (e.g., graphics devices, reactive contexts)</li>
</ul>
<p>The package uses semantics similar to JavaScript promises but with idiomatic R syntax, making it particularly useful for building responsive Shiny applications, coordinating parallel computations, and managing complex asynchronous workflows.</p>
<p>Install the latest version with:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"promises"</span>)</span></code></pre></div>
<section id="new-features" class="level2">
<h2 data-anchor-id="new-features">New Features</h2>
<p>This release includes several new features for more flexible asynchronous programming, OpenTelemetry integration for distributed tracing, and important performance improvements.</p>
<section id="hybrid-synchronousasynchronous-execution-with-hybrid_then" class="level3">
<h3 data-anchor-id="hybrid-synchronousasynchronous-execution-with-hybrid_then">Hybrid Synchronous/Asynchronous Execution with <code>hybrid_then()</code></h3>
<p>The new <code>hybrid_then()</code> function intelligently handles both synchronous values and asynchronous promises, executing callbacks either immediately or asynchronously as appropriate. This is particularly useful when writing functions that need to work seamlessly with mixed inputs:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Works with both regular values and promises</span></span>
<span id="cb2-2">result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">hybrid_then</span>(</span>
<span id="cb2-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">expr =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">possible_promise_or_value</span>(),</span>
<span id="cb2-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">on_success =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(x) x <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb2-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">on_failure =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(err) <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Error occurred"</span>)</span>
<span id="cb2-6">)</span></code></pre></div>
<p>When <code>expr</code> evaluates to a regular value, callbacks execute synchronously on the same tick. When it’s a promise, execution is delegated to <code>then()</code> for proper asynchronous handling.</p>
</section>
<section id="side-effect-operations-with-tee" class="level3">
<h3 data-anchor-id="side-effect-operations-with-tee">Side-Effect Operations with <code>tee</code></h3>
<p>The <code>then()</code> function gains a <code>tee</code> parameter for performing side-effect operations without modifying the promise chain. When <code>tee = TRUE</code>, the callback’s return value is discarded and the original value is propagated:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">promise_that_works</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(value) {</span>
<span id="cb3-3">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Log for debugging without affecting the chain</span></span>
<span id="cb3-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Current value:"</span>, value, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb3-5">  }, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tee =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb3-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(value) {</span>
<span id="cb3-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Original value is still available here</span></span>
<span id="cb3-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">process</span>(value)</span>
<span id="cb3-9">  })</span></code></pre></div>
</section>
<section id="opentelemetry-integration" class="level3">
<h3 data-anchor-id="opentelemetry-integration">OpenTelemetry Integration</h3>
<p><a href="https://rstudio.github.io/promises/">promises</a> now integrates with the <a href="https://github.com/rstudio/otel">otel</a> package to provide observability and distributed tracing for asynchronous operations. Three new functions help manage OpenTelemetry spans across asynchronous boundaries:</p>
<ul>
<li><strong><code>with_otel_span(name, expr, ..., tracer)</code></strong>: Creates an OpenTelemetry span that automatically handles both synchronous and asynchronous operations. A <code>tracer=</code> parameter must be supplied for proper span creation and maximizing performance for when both tracing is enabled and disabled.</li>
<li><strong><code>with_otel_promise_domain(expr)</code></strong>: Creates a promise domain that preserves the active OpenTelemetry span context across asynchronous operations.</li>
<li><strong><code>local_otel_promise_domain()</code></strong>: A <code>local_*()</code> variant of <code>with_otel_promise_domain()</code> that sets up a local promise domain for the current environment.</li>
</ul>
<p>Here’s how to use <code>with_otel_promise_domain()</code> and <code>with_otel_span()</code> in concert:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Setup a consistent OpenTelemetry tracer</span></span>
<span id="cb4-2">my_tracer <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> otel<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_tracer</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-tracer"</span>)</span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># When using OpenTelemetry (otel) spans that must be active across async</span></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># boundaries, wrap the entire promise chain creation within</span></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># `with_otel_promise_domain()`. Once is enough, but it may be nested.</span></span>
<span id="cb4-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_otel_promise_domain</span>({</span>
<span id="cb4-8"></span>
<span id="cb4-9">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Makes an otel span that persists until the promise chain is resolved.</span></span>
<span id="cb4-10">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># This otel span will be reactivated for each step in the promise chain.</span></span>
<span id="cb4-11">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># This otel span will end when the encapsulated promise chain completes.</span></span>
<span id="cb4-12">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_otel_span</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-promise-chain"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tracer =</span> my_tracer, {</span>
<span id="cb4-13"></span>
<span id="cb4-14">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">promise1</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-15">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(\(x) {</span>
<span id="cb4-16">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Make an OpenTelemetry span around `promise2()` promise chain</span></span>
<span id="cb4-17">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># whose parent span is `my-promise-chain`</span></span>
<span id="cb4-18">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_otel_span</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"middle-span"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tracer =</span> my_tracer, {</span>
<span id="cb4-19">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">promise2</span>(x) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-20">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(sideeffect2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tee =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb4-21">        })</span>
<span id="cb4-22">      }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb4-23">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(\(x) {</span>
<span id="cb4-24">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># `with_otel_span()` is not needed for _purely_ synchronous work</span></span>
<span id="cb4-25">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (but may be used if desired!)</span></span>
<span id="cb4-26">        otel<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">start_local_active_span</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"last-span"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tracer =</span> my_tracer)</span>
<span id="cb4-27">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sync3</span>(x)</span>
<span id="cb4-28">      })</span>
<span id="cb4-29"></span>
<span id="cb4-30">  }) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># `my-promise-chain` span ends here</span></span>
<span id="cb4-31"></span>
<span id="cb4-32">})</span></code></pre></div>
<p>When ever the <code>then()</code> callbacks are invoked, the <u>currently active</u> OpenTelemetry span will be captured and reactivated as the active parent for the followup async operation, ensuring accurate tracing across asynchronous operations.</p>
<p>When used correctly, OpenTelemetry traces will show nested spans that accurately represent the asynchronous execution flow:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/promises-1.5.0/logfire-nested.png" class="img-fluid figure-img"></p>
<figcaption>Nested OpenTelemetry spans</figcaption>
</figure>
</div>
<p>When <code>with_otel_promise_domain()</code> does not wrap <code>with_otel_span()</code>, the active span context will be lost across asynchronous boundaries, resulting in disconnected traces with a very fast parent span duration:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/promises-1.5.0/logfire-disconnected.png" class="img-fluid figure-img"></p>
<figcaption>Disconnected OpenTelemetry spans</figcaption>
</figure>
</div>
<p>OpenTelemetry support will be integrated into the next releases of <a href="https://shiny.posit.co/r/">Shiny</a> and <a href="https://plumber2.posit.co/">plumber2</a> (via <a href="https://routr.data-imaginist.com/">routr</a>). Users of those packages will not need to directly call <code>with_otel_promise_domain()</code> or <code>local_otel_promise_domain()</code>.</p>
</section>
</section>
<section id="breaking-changes" class="level2">
<h2 data-anchor-id="breaking-changes">Breaking Changes</h2>
<section id="fixed-nested-promise-domain-behavior" class="level3">
<h3 data-anchor-id="fixed-nested-promise-domain-behavior">Fixed Nested Promise Domain Behavior</h3>
<p>Nested promise domains now correctly invoke in reverse order. Previously, when promise domains were nested, the outer domain would incorrectly take precedence over the inner domain. The innermost (most recently added) domain now properly wraps callbacks first, ensuring consistent scoping behavior:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Inner domain now correctly takes precedence</span></span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_promise_domain</span>(outer_domain, {</span>
<span id="cb5-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_promise_domain</span>(inner_domain, {</span>
<span id="cb5-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">promise</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(callback)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># inner_domain wraps first</span></span>
<span id="cb5-5">  })</span>
<span id="cb5-6">})</span></code></pre></div>
<p>If your code relied on the previous (incorrect) behavior, you will need to adjust the ordering of your promise domains.</p>
</section>
</section>
<section id="other-improvements" class="level2">
<h2 data-anchor-id="other-improvements">Other Improvements</h2>
<section id="performance-enhancements" class="level3">
<h3 data-anchor-id="performance-enhancements">Performance Enhancements</h3>
<p>Promise creation is now faster due to removal of the R6 data structures in favor of lightweight environments, resulting in reduced memory overhead and improved speed for creating and resolving promises.</p>
<p>We’ve also improved the performance of <code>promise_all()</code> by maintaining an internal completion counter, reducing time complexity from <code>O(n²)</code> to <code>O(n)</code> for determining when all promises complete. Additionally, we’ve fixed <code>promise_all()</code> to handle arguments with the same name, ensuring we get a result for every promise provided.</p>
<p>The promises package no longer requires compilation and is now a pure R package, simplifying and reducing installation time, as well as avoiding any additional build requirements.</p>
</section>
<section id="api-improvements" class="level3">
<h3 data-anchor-id="api-improvements">API Improvements</h3>
<p>The package now requires R 4.1 or later, and we’re excited to adopt R 4.1+ recommended syntax! The native pipe (<code>|&gt;</code>) and function shorthand (<code>\(x) fn(x)</code>) are now preferred over promise pipe methods (<code>%...&gt;%</code>, <code>%...!%</code>, <code>%...T&gt;%</code>), which are now superseded.</p>
<p>We’ve also tightened up some of the function signatures: for the argument <code>tee</code>, this is now required to be specified as a named argument <code>catch(promise, onRejected, ..., tee = FALSE)</code>. The <code>tee</code> value must also now be a logical value rather than any truthy value. This named argument is available for <code>then()</code>, <code>catch()</code>, and <code>hybrid_then()</code>.</p>
</section>
<section id="documentation" class="level3">
<h3 data-anchor-id="documentation">Documentation</h3>
<p><strong>Updated for mirai</strong>: The package documentation and vignettes have been comprehensively updated to feature <a href="https://mirai.r-lib.org/">mirai</a> as the recommended approach for launching asynchronous tasks. A new vignette, “Launching tasks with mirai,” provides a complete introduction to using mirai with promises. mirai offers a lightweight, modern alternative to the <a href="https://future.futureverse.org/">future</a> package, utilizing background R processes (daemons) without polling overhead. Examples throughout the documentation now demonstrate mirai usage alongside or in place of future examples.</p>
</section>
</section>
<section id="acknowledgements" class="level2">
<h2 data-anchor-id="acknowledgements">Acknowledgements</h2>
<p>For a complete list of changes, see the <a href="https://rstudio.github.io/promises/news/index.html#promises-150">release notes for v1.5.0</a> and <a href="https://rstudio.github.io/promises/news/index.html#promises-140">v1.4.0</a>.</p>


</section>
</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/promises-1.5.0/</guid>
  <pubDate>Fri, 07 Nov 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/promises-1.5.0/feature.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Build Your First LLM App with Shiny for Python or R</title>
  <dc:creator>Veerle Eeftink - van Leemput</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-header.png" alt="The Shiny Side of LLMs part 3" style="width: 100%; height: auto;">
    </div>
    


<p>You’ve made it to third part of “The Shiny Side of LLMs” series, where we turn everything we’ve learned into something real and interactive! Our weapon of choice: Shiny, of course.</p>
<p>In the first part, <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/">What LLMs Actually Do (and What They Don’t)</a>, we explored what LLMs actually do. We covered how they generate text, what they’re good (and bad) at, and we covered most of the jargon that gets thrown around. Then in part two, <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/">Talking to LLMs: From Prompt to Response</a>, we got practical. You learned how to structure prompts, send them to a model via an API using <a href="https://posit-dev.github.io/chatlas/"><code>chatlas</code></a> or <a href="https://ellmer.tidyverse.org"><code>ellmer</code></a>, and handle the responses in your code. Now it’s time to wrap that logic in an interface your users can love and can actually interact with!</p>
<p>In this post, we’ll cover how to:</p>
<ul>
<li>Structure a basic Shiny app (in both Python and R) with a user interface (UI) and reactive server logic</li>
<li>Connect user input to an LLM call and show the result in the UI</li>
<li>Set up a chat interface</li>
<li>Keep your app responsive while doing lengthy tasks or calculations (yes, a quick guide to async programming!)</li>
<li>Add UI polish like pretty looking value boxes, loading indicators, and error messages</li>
<li>Bonus: deploy your app with a single click to Posit Connect (Cloud) or shinyapps.io</li>
</ul>
<p>By the end of this part, you’ll have your first real, working, LLM-powered app. And most importantly: the knowledge to build many more.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
What are we going to do in this last part?
</div>
</div>
<div class="callout-body-container callout-body">
<p>This part of “The Shiny Side of LLMs” series will build an app called “DeckCheck”: a genius app that gets rid of lengthy unfocused presentations, like a perfect “Presentation Rehearsal Buddy”. The goal: let users upload their <a href="https://quarto.org">Quarto</a> presentation and provide them with feedback on how to make it better.</p>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
No time for the walkthrough?
</div>
</div>
<div class="callout-body-container callout-body">
<p>Want to dive straight into the full app? Head over to the end result.</p>
</div>
</div>
<section id="why-shiny" class="level1">
<h1>Why Shiny?</h1>
<p>We knew pretty early on that “DeckCheck” was going to be an app. Why? Because an app is just easier. People can click buttons, type things, and get results without ever touching code (which is great, because “copy-paste this into your terminal” tends to scare off half the audience).</p>
<p>But ok, why Shiny? You’re reading this on the Shiny blog, so yes, it seems pretty obvious we love it. Still, we have solid reasons:</p>
<ul>
<li>You can go from idea to something you can actually click in almost no time. Shiny is great for prototyping. You can quickly experiment, gather feedback, and gradually build an app out to a production-ready state.</li>
<li>Shiny is reactive by design, which is a fancy way of saying: it keeps track of what depends on what, and only updates what actually needs to change. You don’t need to worry about the logic, which means you can spend more time building your app instead of dealing with state management.</li>
<li>This reactive engine also makes Shiny efficient: outputs only re-render when the stuff they depend on changes.</li>
<li>If your LLM workflow is in Python, there’s Shiny for Python. If it’s in R, there’s Shiny for R. No language wars or conversations about “X is better than Y”. Just pick whatever you’re comfortable with.</li>
<li>You can even drop in your own HTML, CSS, and JavaScript to really make it your own.</li>
</ul>
<p>Alright, enough with the sales pitch. Shiny it is!</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
How Shiny Assistant assists you
</div>
</div>
<div class="callout-body-container callout-body">
<p>Fun fact: this article contains side-by-side examples in both Python and R. To showcase how <a href="https://gallery.shinyapps.io/assistant/">Shiny Assistant</a> can support you in either language, it was used to generate some of the conversions. That’s a neat way to highlight how an LLM can help you get started with Shiny! The true “Shiny Side of LLMs”. Of course result were not always 100% spot on, but luckily there was still a human in the loop.</p>
</div>
</div>
</section>
<section id="optimising-conversations-for-shiny" class="level1">
<h1>Optimising conversations for Shiny</h1>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Getting your API key
</div>
</div>
<div class="callout-body-container callout-body">
<p>Remember you need to <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/#what-do-you-need">grab an API key for your chosen LLM provider</a>. You need this key to authenticate. Store this key as an environment variable. For example, to use Claude from Anthropic, <code>ANTHROPIC_API_KEY=yourkey</code> needs to be in <code>.Renviron</code> or <code>.env</code> file.</p>
</div>
</div>
<p>Going from a script-like workflow to an app requires a different way of thinking. We simply have other expectations from a web app compared to just a regular Python or R script. We want things to be interactive, and ideally we want to have the result instantly. If we click on something, we expect something to happen, fast. Ever encountered a web page that stayed blank for just 5 seconds? How long did that feel? Like 10 minutes? Or didn’t you even stick out the 5 seconds? Yes, you are impatient! You need to see something is happening, and get some visual feedback.</p>
<p>LLMs take your impatience into account, and that’s why most models stream tokens progressively. Instead of making you wait forever for a big wall of text, they start showing you the answer bit by bit, almost like they’re thinking out loud. This streaming feels faster, keeps you engaged, and makes the whole experience way less frustrating. <code>chatlas</code> and <code>ellmer</code> use this streaming capability too and they print the result on the console as soon as words come in. But… a Shiny app doesn’t have a console! So what to do?</p>
<p>Where the <code>chat()</code> method does not return any results until the entire response is received and only prints the streaming results to the console, the <code>stream()</code> method can process the response as it arrives. It’s perfect for something like a Shiny chat window. You simply replace the <code>chat()</code> method with the <code>stream()</code> method, which returns something called a “generator”. A generator is a function that can pause (<code>yield</code>) and resume later, remembering where it left off. That’s handy because it lets your code:</p>
<ol type="1">
<li>Process text as it arrives (aka, by chunk) instead of waiting for the whole thing.</li>
<li>Pause between chunks without blocking other things.</li>
<li>Keep its place so it can pick up right where it left off when the next chunk arrives.</li>
</ol>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<p>In the <code>chatlas</code> documentation you can read more about <a href="https://posit-dev.github.io/chatlas/get-started/stream.html">streams</a>.</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic</span>
<span id="cb1-2"></span>
<span id="cb1-3">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb1-4">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb1-5">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"You are a presentation coach for data scientists. You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span>,</span>
<span id="cb1-6">)</span>
<span id="cb1-7"></span>
<span id="cb1-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set model parameters (optional)</span></span>
<span id="cb1-9">chat.set_model_params(</span>
<span id="cb1-10">    temperature<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb1-11">)</span>
<span id="cb1-12"></span>
<span id="cb1-13">stream <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chat.stream(</span>
<span id="cb1-14">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""I'm working on a presentation with the title: 'The Shiny Side of LLMs'.</span></span>
<span id="cb1-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        Please evaluate the clarity, tone, and relevance of this title for the intended audience.</span></span>
<span id="cb1-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        For context, this is a 10-minute lightning talk at posit::conf(2025).</span></span>
<span id="cb1-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        The audience is Python and R users who are curious about AI and large language models,</span></span>
<span id="cb1-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        but not all of them have a deep technical background.</span></span>
<span id="cb1-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        </span></span>
<span id="cb1-20"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        The talk uses Shiny as a way to explore and demo LLMs in practice.</span></span>
<span id="cb1-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        </span></span>
<span id="cb1-22"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        Return your answer as a JSON array of objects, where each object has the following keys:</span></span>
<span id="cb1-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        </span></span>
<span id="cb1-24"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        - 'aspect': one of 'clarity', 'tone', or 'relevance'</span></span>
<span id="cb1-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        - 'feedback': your concise assessment</span></span>
<span id="cb1-26"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        - 'suggestion': an optional improvement if applicable"""</span></span>
<span id="cb1-27">)</span>
<span id="cb1-28"></span>
<span id="cb1-29"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> chunk <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> stream:</span>
<span id="cb1-30">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(chunk)</span></code></pre></div>
<p>The object that gets returned by the <a href="https://posit-dev.github.io/chatlas/reference/Chat.html#stream"><code>stream()</code></a> method is a <a href="https://wiki.python.org/moin/Generators">generator</a>.</p>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<p>In the <code>ellmer</code> documentation you can read more about <a href="https://ellmer.tidyverse.org/articles/streaming-async.html">streaming</a>.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb2-2"></span>
<span id="cb2-3">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb2-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb2-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"You are a presentation coach for data scientists.</span></span>
<span id="cb2-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span>,</span>
<span id="cb2-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span>(</span>
<span id="cb2-8">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temperature =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb2-9">  )</span>
<span id="cb2-10">)</span>
<span id="cb2-11"></span>
<span id="cb2-12">stream <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stream</span>(</span>
<span id="cb2-13">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I'm working on a presentation with the title: 'The Shiny Side of LLMs'.</span></span>
<span id="cb2-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">Please evaluate the clarity, tone, and relevance of this title for the intended audience.</span></span>
<span id="cb2-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">For context, this is a 10-minute lightning talk at posit::conf(2025).</span></span>
<span id="cb2-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">The audience is Python and R users who are curious about AI and large language models,</span></span>
<span id="cb2-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">but not all of them have a deep technical background.</span></span>
<span id="cb2-18"></span>
<span id="cb2-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">The talk uses Shiny as a way to explore and demo LLMs in practice.</span></span>
<span id="cb2-20"></span>
<span id="cb2-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">Return your answer as a JSON array of objects, where each object has the following keys:</span></span>
<span id="cb2-22"></span>
<span id="cb2-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">- 'aspect': one of 'clarity', 'tone', or 'relevance'</span></span>
<span id="cb2-24"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">- 'feedback': your concise assessment</span></span>
<span id="cb2-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">- 'suggestion': an optional improvement if applicable"</span></span>
<span id="cb2-26">)</span>
<span id="cb2-27"></span>
<span id="cb2-28">coro<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">loop</span>(</span>
<span id="cb2-29">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> (chunk <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> stream) {</span>
<span id="cb2-30">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(chunk)</span>
<span id="cb2-31">  }</span>
<span id="cb2-32">)</span></code></pre></div>
<p>The object that gets returned by the <a href="https://ellmer.tidyverse.org/reference/Chat.html#method-stream-"><code>stream()</code></a> method is a <a href="https://coro.r-lib.org/articles/generator.html#generators">coro generator</a>.</p>
</div>
</div>
</div>
<p>Streaming is great for things like chatbots, live transcription, or anything where seeing text appear in real time feels natural. It’s perfect for conversations, or when the answer is long and you don’t want to keep users staring at a blank screen.</p>
<p>In apps like “DeckCheck”, where we’re analysing a Quarto presentation behind the scenes and then showing a finished result, streaming doesn’t really add much. Users expect a clear, polished answer all at once. Not a JSON, but ready-to-go value boxes, graphs and tables. So in this case, a smooth loading indicator or progress bar will probably satisfy impatient users and we can stick to our <code>chat()</code> method. Don’t forget the <code>stream()</code> method though: we’ll use that a little bit later in a small demo chatbot.</p>
<p>Whether we’re streaming or not, users simply have to wait for an LLM response and you can keep them entertained while they’re doing that. But another important thing related to a long waiting time is when your app is used by multiple users at the same time. They might ask things from the model at the same time too, meaning that you’re dealing with concurrent chat sessions. If you would just use <code>chat()</code> or <code>stream()</code>, the Shiny app (or technically speaking: the Python or R session running the Shiny app) will be blocked for other users for the duration of each response. And that’s not cool. The more users are concurrently using your app, the longer the queue gets, and the longer users have to wait. This is <strong>synchronous</strong> behaviour. We rather deal <strong>asynchronously</strong> (aka <strong>async</strong>) with a model’s responses. It means that we can receive responses at the same time, in parallel. To use async chat, we need to call <code>chat_async()</code> / <code>stream_async()</code> instead of <code>chat()</code> / <code>stream()</code>. The <code>_async</code> variants take the same arguments but returns a <strong>coroutine</strong> object, (aka a placeholder for something that will come) instead of the actual response.</p>
<p>So ideally, asking a question to an LLM would look something like this in our Shiny application:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-2-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-1" aria-controls="tabset-2-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-2-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-2" aria-controls="tabset-2-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-2-1" class="tab-pane active" aria-labelledby="tabset-2-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note for Positron/Jupyter users
</div>
</div>
<div class="callout-body-container callout-body">
<p>Positron and Jupyter already run their own event loop, so <code>asyncio.run(main())</code> will fail with a runtime error. Instead of wrapping in <code>asyncio.run</code>, you can just do: <code>await main()</code> at the top level.</p>
</div>
</div>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> asyncio</span>
<span id="cb3-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic</span>
<span id="cb3-3"></span>
<span id="cb3-4">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb3-5">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb3-6">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"You are a presentation coach for data scientists. You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span>,</span>
<span id="cb3-7">)</span>
<span id="cb3-8"></span>
<span id="cb3-9"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> main():</span>
<span id="cb3-10">    response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.chat_async(</span>
<span id="cb3-11">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""I'm working on a presentation with the title: 'The Shiny Side of LLMs'.</span></span>
<span id="cb3-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        Please evaluate the clarity, tone, and relevance of this title for the intended audience.</span></span>
<span id="cb3-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        For context, this is a 10-minute lightning talk at posit::conf(2025).</span></span>
<span id="cb3-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        The audience is Python and R users who are curious about AI and large language models,</span></span>
<span id="cb3-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        but not all of them have a deep technical background.</span></span>
<span id="cb3-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        </span></span>
<span id="cb3-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        The talk uses Shiny as a way to explore and demo LLMs in practice.</span></span>
<span id="cb3-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        </span></span>
<span id="cb3-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        Return your answer as a JSON array of objects, where each object has the following keys:</span></span>
<span id="cb3-20"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        </span></span>
<span id="cb3-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        - 'aspect': one of 'clarity', 'tone', or 'relevance'</span></span>
<span id="cb3-22"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        - 'feedback': your concise assessment</span></span>
<span id="cb3-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        - 'suggestion': an optional improvement if applicable"""</span></span>
<span id="cb3-24">    )</span>
<span id="cb3-25">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(response)</span>
<span id="cb3-26"></span>
<span id="cb3-27">asyncio.run(main())</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb4-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> [                                                                                                          </span></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   {                                                                                                        </span></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "aspect": "clarity",                                                                                   </span></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "feedback": "The title is somewhat ambiguous - 'shiny side' could refer to positive aspects of LLMs or </span></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> the Shiny framework itself. For an audience not deeply familiar with both topics, this creates confusion   </span></span>
<span id="cb4-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> about the main focus.",                                                                                    </span></span>
<span id="cb4-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "suggestion": "Consider titles like 'Exploring LLMs with Shiny' or 'Building LLM Demos in Shiny' to    </span></span>
<span id="cb4-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> clearly establish that Shiny is your vehicle for demonstrating LLMs."                                      </span></span>
<span id="cb4-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                                       </span></span>
<span id="cb4-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   {                                                                                                        </span></span>
<span id="cb4-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "aspect": "tone",                                                                                      </span></span>
<span id="cb4-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "feedback": "The tone is appropriately casual and engaging for a lightning talk, with a clever play on </span></span>
<span id="cb4-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> words that fits the conference atmosphere. However, it may be too playful for conveying the practical valu </span></span>
<span id="cb4-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> of the content.",                                                                                          </span></span>
<span id="cb4-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "suggestion": "Balance the wordplay with clearer value proposition, such as 'The Shiny Side of LLMs:   </span></span>
<span id="cb4-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Interactive AI Exploration' to maintain engagement while signaling practical benefits."                    </span></span>
<span id="cb4-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                                       </span></span>
<span id="cb4-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   {                                                                                                        </span></span>
<span id="cb4-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "aspect": "relevance",                                                                                 </span></span>
<span id="cb4-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "feedback": "Highly relevant to the posit::conf audience who knows Shiny well, but the connection      </span></span>
<span id="cb4-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> between Shiny and LLMs isn't immediately clear from the title alone. Given the 10-minute format, you need  </span></span>
<span id="cb4-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> attendees to quickly understand the value.",                                                               </span></span>
<span id="cb4-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "suggestion": "Make the connection more explicit with alternatives like 'Hands-On LLM Exploration with </span></span>
<span id="cb4-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Shiny Apps' or 'From Prompts to Production: LLMs in Shiny' to immediately communicate the practical        </span></span>
<span id="cb4-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> application."                                                                                              </span></span>
<span id="cb4-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   }                                                                                                        </span></span>
<span id="cb4-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> ]                                                                                                          </span></span>
<span id="cb4-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;chatlas._chat.ChatResponseAsync object at 0x111a5c560&gt;</span></span>
<span id="cb4-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
<p><a href="https://posit-dev.github.io/chatlas/reference/Chat.html#chatlas.Chat.chat_async"><code>chat_async()</code></a> returns a coroutine object, which is basically a special kind of function that runs asynchronously. It doesn’t do the work right away when you call it, but it gives you this object that you can later “await” to actually get the result. If you’re running regular (non-asynchronous) code, you use <code>asyncio.run()</code> to start and wait for the task to finish.</p>
</div>
<div id="tabset-2-2" class="tab-pane" aria-labelledby="tabset-2-2-tab">
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(promises)</span>
<span id="cb5-3"></span>
<span id="cb5-4">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb5-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb5-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"You are a presentation coach for data scientists.</span></span>
<span id="cb5-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span></span>
<span id="cb5-8">)</span>
<span id="cb5-9"></span>
<span id="cb5-10">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_async</span>(</span>
<span id="cb5-11">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I'm working on a presentation with the title: 'The Shiny Side of LLMs'.</span></span>
<span id="cb5-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">Please evaluate the clarity, tone, and relevance of this title for the intended audience.</span></span>
<span id="cb5-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">For context, this is a 10-minute lightning talk at posit::conf(2025).</span></span>
<span id="cb5-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">The audience is Python and R users who are curious about AI and large language models,</span></span>
<span id="cb5-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">but not all of them have a deep technical background.</span></span>
<span id="cb5-16"></span>
<span id="cb5-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">The talk uses Shiny as a way to explore and demo LLMs in practice.</span></span>
<span id="cb5-18"></span>
<span id="cb5-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">Return your answer as a JSON array of objects, where each object has the following keys:</span></span>
<span id="cb5-20"></span>
<span id="cb5-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">- 'aspect': one of 'clarity', 'tone', or 'relevance'</span></span>
<span id="cb5-22"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">- 'feedback': your concise assessment</span></span>
<span id="cb5-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">- 'suggestion': an optional improvement if applicable"</span></span>
<span id="cb5-24">) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%...&gt;%</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>()</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "```json\n[\n  {\n    \"aspect\": \"clarity\",\n    \"feedback\": \"The title is somewhat ambiguous - 'shiny side' could mean positive aspects of LLMs or reference the Shiny framework. The wordplay may confuse rather than clarify the content focus.\",\n    \"suggestion\": \"Consider 'Building LLM Demos with Shiny' or 'Interactive LLM Exploration Using Shiny' to clearly communicate both the tool and topic.\"\n  },\n  {\n    \"aspect\": \"tone\",\n    \"feedback\": \"The playful wordplay fits well with the lightning talk format and conference atmosphere. It's approachable and not intimidating for audiences with varying technical backgrounds.\",\n    \"suggestion\": null\n  },\n  {\n    \"aspect\": \"relevance\",\n    \"feedback\": \"Highly relevant for posit::conf audience who are familiar with Shiny. The title connects a trending AI topic with a beloved R/Python tool, making LLMs accessible to the community.\",\n    \"suggestion\": \"Consider adding a subtitle for context: 'The Shiny Side of LLMs: Interactive Demos for AI Exploration' to enhance relevance while keeping the clever wordplay.\"\n  }\n]\n```"</span></span></code></pre></div>
</details>
</div>
<p><a href="https://ellmer.tidyverse.org/reference/Chat.html#method-chat-async-"><code>chat_async()</code></a> starts the work and returns a promise, this special kind of placeholder. Then <a href="https://rstudio.github.io/promises/reference/pipes.html"><code>%...&gt;%</code></a> attaches the next step, like printing the result, once it’s ready. This keeps your R session running without waiting or freezing. Note that it resolves to a string (probably Markdown), which is slightly different than just using the <code>chat()</code> method. This is also why the output looks a little bit different compared to <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/">part two of this series</a>.</p>
</div>
</div>
</div>
<p>And before you’re thinking: “hey, we were using structured output <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/">in the last part</a>, right?” Yes! Luckily there’s also a method called <code>chat_structured_async()</code> (see docs for <a href="https://posit-dev.github.io/chatlas/reference/Chat.html#chatlas.Chat.chat_structured_async">Python</a> and <a href="https://ellmer.tidyverse.org/reference/Chat.html#method-extract-data-async-">R</a>). How convenient! We’ll use that a little bit later.</p>
</section>
<section id="chatting-with-an-llm-via-shiny" class="level1">
<h1>Chatting with an LLM via Shiny</h1>
<p>Never developed a Shiny app before? That’s ok! Before building our DeckCheck app, we’ll start with a very basic example that allows you to chat with any LLM, just like you type in your question at ChatGPT. And while this series isn’t about building “just a chatbot”, you can perfectly do so with Shiny. Minimal code required to get started. If you have experience with Shiny this code won’t have much surprises, but if you’re new to Shiny there’s a mini crash-course-like explanation below the code.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-3-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-1" aria-controls="tabset-3-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-3-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-2" aria-controls="tabset-3-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-3-1" class="tab-pane active" aria-labelledby="tabset-3-1-tab">
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> App, ui</span>
<span id="cb7-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic</span>
<span id="cb7-3"></span>
<span id="cb7-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define UI</span></span>
<span id="cb7-5">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fluid(</span>
<span id="cb7-6">    ui.h1(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DeckCheck"</span>),</span>
<span id="cb7-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Card with chat component</span></span>
<span id="cb7-8">    ui.card(</span>
<span id="cb7-9">        ui.card_header(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Get started"</span>),</span>
<span id="cb7-10">        ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ask me anything about your presentation 💡"</span>),</span>
<span id="cb7-11">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Chat component</span></span>
<span id="cb7-12">        ui.chat_ui(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my_chat"</span>),</span>
<span id="cb7-13">    ),</span>
<span id="cb7-14">)</span>
<span id="cb7-15"></span>
<span id="cb7-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define server</span></span>
<span id="cb7-17"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb7-18">    chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.Chat(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my_chat"</span>)</span>
<span id="cb7-19"></span>
<span id="cb7-20">    chat_client <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb7-21">        model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb7-22">        system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"You are a presentation coach for data scientists. You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span>,</span>
<span id="cb7-23">    )</span>
<span id="cb7-24"></span>
<span id="cb7-25">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@chat.on_user_submit</span></span>
<span id="cb7-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> handle_user_input(user_input: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb7-27">        response <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat_client.stream_async(user_input)</span>
<span id="cb7-28">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.append_message_stream(response)</span>
<span id="cb7-29"></span>
<span id="cb7-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create app</span></span>
<span id="cb7-31">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
<p>In the above code <code>app_ui</code> defines the front end of the app. It’s everything the user sees and interacts with. Here, it’s created with <code>ui.page_fluid()</code> and contains a heading with our app title, a <code>ui.card()</code> with a header (<code>ui.card_header()</code>) and short paragraph (<code>ui.p()</code>). Below, there’s a <code>ui.chat_ui()</code> component that serves as the chat interface. The <code>server</code> function controls the app’s back end and this part is responsible for how it responds to user actions. Inside this server, we first create a <code>chat</code> object that connects to the chat UI via its ID (the o-so original <code>"my_chat"</code>). Then, we create a <code>chat_client</code> object via <code>chatlas</code>.</p>
<p>The key part is the <code>@chat.on_user_submit</code> decorator. <code>.on_user_submit</code> lets you define a function that runs when the user hits “send” in the chat UI. This function, in our case called <code>handle_user_input</code>, sends the message asynchronously to the model via <code>chat_client.stream_async()</code> and streams the reply back into the chat UI with <code>append_message_stream()</code>. Under the hood, Shiny makes use of the <a href="https://github.com/posit-dev/shinychat"><code>shinychat</code></a> package.</p>
<p>When we asks a question (e.g.&nbsp;our simple “I’m working on a presentation with the title: ‘The Shiny Side of LLMs’. What’s your feedback just based on that title?”) we get a nicely formatted response back:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-chat-py.png" class="img-fluid"></p>
</div>
<div id="tabset-3-2" class="tab-pane" aria-labelledby="tabset-3-2-tab">
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shiny)</span>
<span id="cb8-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bslib)</span>
<span id="cb8-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb8-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shinychat)</span>
<span id="cb8-5"></span>
<span id="cb8-6">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fluid</span>(</span>
<span id="cb8-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bootswatch =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flatly"</span>),</span>
<span id="cb8-8"></span>
<span id="cb8-9">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># App title</span></span>
<span id="cb8-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">h1</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DeckCheck"</span>),</span>
<span id="cb8-11"></span>
<span id="cb8-12">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create a card</span></span>
<span id="cb8-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb8-14">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Get started"</span>),</span>
<span id="cb8-15">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ask me anything about your presentation 💡"</span>),</span>
<span id="cb8-16"></span>
<span id="cb8-17">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Chat component</span></span>
<span id="cb8-18">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_mod_ui</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my_chat"</span>)</span>
<span id="cb8-19">  )</span>
<span id="cb8-20">)</span>
<span id="cb8-21"></span>
<span id="cb8-22">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb8-23">  chat_client <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb8-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb8-25">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"You are a presentation coach for data scientists. </span></span>
<span id="cb8-26"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span></span>
<span id="cb8-27">  )</span>
<span id="cb8-28"></span>
<span id="cb8-29">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_mod_server</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my_chat"</span>, chat_client)</span>
<span id="cb8-30">}</span>
<span id="cb8-31"></span>
<span id="cb8-32"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Custom streaming
</div>
</div>
<div class="callout-body-container callout-body">
<p>If you ever wanted to build something more custom, <code>shinychat::markdown_stream()</code> would let you stream model output into any Shiny interface, chat or not.</p>
</div>
</div>
<p>In the above code, <code>ui</code> defines the front end of the app. It’s everything the user sees and interacts with. Here, it’s created with <code>bslib</code>’s <code>page_fluid()</code> with a “Flatly” Bootstrap theme for styling. Don’t like Flatly? There are many <a href="https://rstudio.github.io/bslib/articles/theming/index.html">themes</a> to choose from! And if you don’t like any, you can just skip the <code>theme</code> argument or get started with <a href="https://shiny.posit.co/r/articles/build/css/">custom CSS</a>.</p>
<p>After specifying the theme, there’s a heading with our app title, followed by a <code>card()</code> containing a header, a short description, and most importantly, the chat interface provided by <code>chat_mod_ui("my_chat")</code> from the <a href="https://github.com/posit-dev/shinychat"><code>shinychat</code></a> package.</p>
<p>The <code>server</code> function controls the app’s back end and this part is responsible for how it responds to user actions. Inside this server, we first create a <code>chat_client</code> object using <code>chat_anthropic()</code>. Then we connect this chat object to the UI with <code>chat_mod_server("my_chat", chat_client)</code> via its ID (<code>"my_chat"</code>).</p>
<p><code>chat_mod_ui()</code> and <code>chat_mod_server()</code> together form a Shiny module. This module handles sending user messages to the model, receiving responses, and streaming those responses back into the UI, all asynchronously. The async streaming happens “under the hood,” so you don’t have to manually manage partial chunks of text or async calls yourself.</p>
<p>When we asks a question (e.g.&nbsp;our simple “I’m working on a presentation with the title: ‘The Shiny Side of LLMs’. What’s your feedback just based on that title?”) we get a nicely formatted response back:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-chat-r.png" class="img-fluid"></p>
</div>
</div>
</div>
</section>
<section id="building-deckcheck" class="level1">
<h1>Building DeckCheck</h1>
<p>All the ingredients are there now: we know how to programmatically talk to an LLM, we can make an informed choice when it comes to streaming (or not streaming) and async usage, and we’ve seen how to combine it in a simple chat interface (with a bit of help from <code>shinychat</code>). Time to apply that knowledge to our DeckCheck app.</p>
<p>But, first things first… A design. Nothing too fancy, just a quick conceptual drawing of what our app will look like. No matter what kind of app you’re developing, this is always the (and honestly, often overlooked) first step. If you know what you’re building towards, it’s way easier to start coding. Here’s our design:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-design-1.png" class="img-fluid"></p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-design-2.png" class="img-fluid"></p>
<p>We have a section where users can upload information (most importantly, the Quarto presentation itself, but also the required information like audience), there are some value boxes, there’s a graph, and a table. Elements you will frequently encounter when developing data-savvy apps. For a reason, of course, as we want to provide you with all the necessary building blocks.</p>
<p>To bring this design to life, here’s what we’re working towards:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-deckcheck-full.gif" class="img-fluid"></p>
<p>But first things first: let’s start with building the basic UI before we connect the server part to it.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-4-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-1" aria-controls="tabset-4-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-4-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-2" aria-controls="tabset-4-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-4-1" class="tab-pane active" aria-labelledby="tabset-4-1-tab">
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> App, ui</span>
<span id="cb9-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> shinyswatch</span>
<span id="cb9-3"></span>
<span id="cb9-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb9-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Icons</span></span>
<span id="cb9-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb9-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># SVG icon copied from https://icons.getbootstrap.com/icons/file-slides-fill/</span></span>
<span id="cb9-8">file_slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-slides-fill" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb9-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M7 7.78V5.22c0-.096.106-.156.19-.106l2.13 1.279a.125.125 0 0 1 0 .214l-2.13 1.28A.125.125 0 0 1 7 7.778z"/&gt;</span></span>
<span id="cb9-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M12 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M5 4h6a.5.5 0 0 1 .496.438l.5 4A.5.5 0 0 1 11.5 9h-3v2.016c.863.055 1.5.251 1.5.484 0 .276-.895.5-2 .5s-2-.224-2-.5c0-.233.637-.429 1.5-.484V9h-3a.5.5 0 0 1-.496-.562l.5-4A.5.5 0 0 1 5 4"/&gt;</span></span>
<span id="cb9-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb9-12"></span>
<span id="cb9-13">file_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-code-fill" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb9-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M12 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M6.646 5.646a.5.5 0 1 1 .708.708L5.707 8l1.647 1.646a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708zm2.708 0 2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L10.293 8 8.646 6.354a.5.5 0 1 1 .708-.708"/&gt;</span></span>
<span id="cb9-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb9-16"></span>
<span id="cb9-17">file_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-image-fill" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb9-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M4 0h8a2 2 0 0 1 2 2v8.293l-2.73-2.73a1 1 0 0 0-1.52.127l-1.889 2.644-1.769-1.062a1 1 0 0 0-1.222.15L2 12.292V2a2 2 0 0 1 2-2m4.002 5.5a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0"/&gt;</span></span>
<span id="cb9-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M10.564 8.27 14 11.708V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-.293l3.578-3.577 2.56 1.536 2.426-3.395z"/&gt;</span></span>
<span id="cb9-20"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb9-21"></span>
<span id="cb9-22">robot <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-robot" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb9-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135"/&gt;</span></span>
<span id="cb9-24"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5"/&gt;</span></span>
<span id="cb9-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb9-26"></span>
<span id="cb9-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb9-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny App</span></span>
<span id="cb9-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb9-30">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fillable(</span>
<span id="cb9-31">    ui.layout_sidebar(</span>
<span id="cb9-32">        ui.sidebar(</span>
<span id="cb9-33">            ui.div(</span>
<span id="cb9-34">                ui.p(ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hey, I am DeckCheck!"</span>)),</span>
<span id="cb9-35">                ui.p(</span>
<span id="cb9-36">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""I can help you improve your Quarto presentations by analysing them and suggesting improvements. Before I can do that, I need some information about your presentation."""</span></span>
<span id="cb9-37">                ),</span>
<span id="cb9-38">            ),</span>
<span id="cb9-39">            ui.input_file(</span>
<span id="cb9-40">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span>,</span>
<span id="cb9-41">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Upload your Quarto presentation"</span>,</span>
<span id="cb9-42">                accept<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmdx"</span>],</span>
<span id="cb9-43">                multiple<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb9-44">            ),</span>
<span id="cb9-45">            ui.input_text_area(</span>
<span id="cb9-46">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>,</span>
<span id="cb9-47">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Describe your audience"</span>,</span>
<span id="cb9-48">                height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"150px"</span>,</span>
<span id="cb9-49">                placeholder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span>,</span>
<span id="cb9-50">            ),</span>
<span id="cb9-51">            ui.input_numeric(</span>
<span id="cb9-52">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time cap for the presentation (minutes)"</span>, value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb9-53">            ),</span>
<span id="cb9-54">            ui.input_text(</span>
<span id="cb9-55">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>,</span>
<span id="cb9-56">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Type of talk"</span>,</span>
<span id="cb9-57">                placeholder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. lightning talk, workshop, or keynote"</span>,</span>
<span id="cb9-58">            ),</span>
<span id="cb9-59">            ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Event name"</span>, placeholder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. posit::conf(2025)"</span>),</span>
<span id="cb9-60">            ui.input_task_button(</span>
<span id="cb9-61">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>,</span>
<span id="cb9-62">                icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(robot),</span>
<span id="cb9-63">                label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyse presentation"</span>,</span>
<span id="cb9-64">            ),</span>
<span id="cb9-65">            width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>,</span>
<span id="cb9-66">        ),</span>
<span id="cb9-67">        ui.layout_column_wrap(</span>
<span id="cb9-68">            ui.value_box(</span>
<span id="cb9-69">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Showtime"</span>,</span>
<span id="cb9-70">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"9 minutes"</span>,</span>
<span id="cb9-71">                showcase<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(file_slides),</span>
<span id="cb9-72">                theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span>,</span>
<span id="cb9-73">            ),</span>
<span id="cb9-74">            ui.value_box(</span>
<span id="cb9-75">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Code Savviness"</span>,</span>
<span id="cb9-76">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"15%"</span>,</span>
<span id="cb9-77">                showcase<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(file_code),</span>
<span id="cb9-78">                theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span>,</span>
<span id="cb9-79">            ),</span>
<span id="cb9-80">            ui.value_box(</span>
<span id="cb9-81">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Image Presence"</span>,</span>
<span id="cb9-82">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"7%"</span>,</span>
<span id="cb9-83">                showcase<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(file_image),</span>
<span id="cb9-84">                theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span>,</span>
<span id="cb9-85">            ),</span>
<span id="cb9-86">            width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,</span>
<span id="cb9-87">            fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb9-88">        ),</span>
<span id="cb9-89">        ui.layout_column_wrap(</span>
<span id="cb9-90">            ui.card(</span>
<span id="cb9-91">                ui.card_header(ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Scores per category"</span>)),</span>
<span id="cb9-92">                ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My beautiful interactive plot..."</span>),</span>
<span id="cb9-93">            ),</span>
<span id="cb9-94">            ui.card(</span>
<span id="cb9-95">                ui.card_header(ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Suggested improvements per category"</span>)),</span>
<span id="cb9-96">                ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My beatiful table..."</span>),</span>
<span id="cb9-97">            ),</span>
<span id="cb9-98">            width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb9-99">            fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb9-100">        ),</span>
<span id="cb9-101">    ),</span>
<span id="cb9-102">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Bootswatch theme</span></span>
<span id="cb9-103">    theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shinyswatch.theme.flatly,</span>
<span id="cb9-104">)</span>
<span id="cb9-105"></span>
<span id="cb9-106"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb9-107">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Empty for demo purposes</span></span>
<span id="cb9-108">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">pass</span></span>
<span id="cb9-109"></span>
<span id="cb9-110">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
</div>
<div id="tabset-4-2" class="tab-pane" aria-labelledby="tabset-4-2-tab">
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb10-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shiny)</span>
<span id="cb10-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bslib)</span>
<span id="cb10-3"></span>
<span id="cb10-4">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fillable</span>(</span>
<span id="cb10-5">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## General theme and styles</span></span>
<span id="cb10-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bootswatch =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flatly"</span>),</span>
<span id="cb10-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layout_sidebar</span>(</span>
<span id="cb10-8">    <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Sidebar content</span></span>
<span id="cb10-9">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sidebar =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sidebar</span>(</span>
<span id="cb10-10">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>,</span>
<span id="cb10-11">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Open sidebar on mobile devices and show above content</span></span>
<span id="cb10-12">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">open =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mobile =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"always-above"</span>),</span>
<span id="cb10-13">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hey, I am DeckCheck!"</span>)),</span>
<span id="cb10-14">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(</span>
<span id="cb10-15">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I can help you improve your Quarto presentations by analysing them and suggesting improvements.</span></span>
<span id="cb10-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      Before I can do that, I need some information about your presentation."</span></span>
<span id="cb10-17">      ),</span>
<span id="cb10-18">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fileInput</span>(</span>
<span id="cb10-19">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span>,</span>
<span id="cb10-20">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Upload your Quarto presentation"</span>,</span>
<span id="cb10-21">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accept =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmdx"</span>)</span>
<span id="cb10-22">      ),</span>
<span id="cb10-23">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textAreaInput</span>(</span>
<span id="cb10-24">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>,</span>
<span id="cb10-25">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"150px"</span>,</span>
<span id="cb10-26">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Describe your audience"</span>,</span>
<span id="cb10-27">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">placeholder =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb10-28">      ),</span>
<span id="cb10-29">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">numericInput</span>(</span>
<span id="cb10-30">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>,</span>
<span id="cb10-31">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time cap for the presentation (minutes)"</span>,</span>
<span id="cb10-32">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span></span>
<span id="cb10-33">      ),</span>
<span id="cb10-34">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textInput</span>(</span>
<span id="cb10-35">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>,</span>
<span id="cb10-36">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Type of talk"</span>,</span>
<span id="cb10-37">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">placeholder =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. lightning talk, workshop, or keynote"</span></span>
<span id="cb10-38">      ),</span>
<span id="cb10-39">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textInput</span>(</span>
<span id="cb10-40">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>,</span>
<span id="cb10-41">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Event name"</span>,</span>
<span id="cb10-42">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">placeholder =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. posit::conf(2025)"</span></span>
<span id="cb10-43">      ),</span>
<span id="cb10-44">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">input_task_button</span>(</span>
<span id="cb10-45">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">id =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>,</span>
<span id="cb10-46">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> shiny<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tagList</span>(</span>
<span id="cb10-47">          bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robot"</span>),</span>
<span id="cb10-48">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyse presentation"</span></span>
<span id="cb10-49">        ),</span>
<span id="cb10-50">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label_busy =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DeckCheck is checking..."</span>,</span>
<span id="cb10-51">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"default"</span></span>
<span id="cb10-52">      )</span>
<span id="cb10-53">    ),</span>
<span id="cb10-54">    <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Main content</span></span>
<span id="cb10-55">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layout_column_wrap</span>(</span>
<span id="cb10-56">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb10-57">      <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">### Value boxes for metrics</span></span>
<span id="cb10-58">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">value_box</span>(</span>
<span id="cb10-59">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Showtime"</span>,</span>
<span id="cb10-60">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"9 minutes"</span>,</span>
<span id="cb10-61">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">showcase =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-slides"</span>),</span>
<span id="cb10-62">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span></span>
<span id="cb10-63">      ),</span>
<span id="cb10-64">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">value_box</span>(</span>
<span id="cb10-65">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Code Savviness"</span>,</span>
<span id="cb10-66">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"15%"</span>,</span>
<span id="cb10-67">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">showcase =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-code"</span>),</span>
<span id="cb10-68">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span></span>
<span id="cb10-69">      ),</span>
<span id="cb10-70">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">value_box</span>(</span>
<span id="cb10-71">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Image Presence"</span>,</span>
<span id="cb10-72">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"7%"</span>,</span>
<span id="cb10-73">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">showcase =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-image"</span>),</span>
<span id="cb10-74">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span></span>
<span id="cb10-75">      )</span>
<span id="cb10-76">    ),</span>
<span id="cb10-77">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layout_column_wrap</span>(</span>
<span id="cb10-78">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb10-79">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb10-80">      <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">### Graph with scoring metrics</span></span>
<span id="cb10-81">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb10-82">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Scores per category"</span>)),</span>
<span id="cb10-83">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My beatiful interactive plot..."</span>)</span>
<span id="cb10-84">      ),</span>
<span id="cb10-85">      <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">### Table with suggested improvements</span></span>
<span id="cb10-86">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb10-87">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Suggested improvements per category"</span>)),</span>
<span id="cb10-88">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My beatiful table..."</span>)</span>
<span id="cb10-89">      )</span>
<span id="cb10-90">    )</span>
<span id="cb10-91">  )</span>
<span id="cb10-92">)</span>
<span id="cb10-93"></span>
<span id="cb10-94">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {}</span>
<span id="cb10-95"></span>
<span id="cb10-96"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
</div>
</div>
</div>
<p>No matter what language you use to display this basic UI, the result is the same:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-ui-only.png" class="img-fluid"></p>
<p>That’s already a start! Of course it doesn’t do anything yet and is filled with placeholders, so we need some logic in the server part. The main engine behind DeckCheck is our conversation with the LLM. This logic is <em>almost</em> a copy-paste from part two, <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/">Talking to LLMs: From Prompt to Response</a>, combined with what we learned earlier in this article about async.</p>
<p>The star of this main engine is something called “extended task”. As mentioned previously, by default, Shiny runs code synchronously. That means if we ask it to render a Quarto presentation or send a request to an LLM, the app would block until that job is done. The whole interface would freeze. That’s no fun for the user. That’s why Shiny has an option to run extended tasks (<a href="https://shiny.posit.co/py/docs/nonblocking.html"><code>extended_task</code></a> in Python, <a href="https://shiny.posit.co/r/articles/improve/nonblocking/"><code>ExtendedTask</code></a> in R). It lets us run non-blocking jobs asynchronously in the background, so our app can stay responsive. It works together with a special action button, <code>input_task_button</code>, which is designed to trigger long running tasks. In order for this button to work you need to <strong>bind</strong> the button to the extended task with <code>bind_task_button</code>.</p>
<p>For DeckCheck, there are two main jobs to do:</p>
<ol type="1">
<li><strong>Render the presentation (we call this task<code>quarto_task</code>)</strong>
<ul>
<li>Takes the uploaded Quarto file.</li>
<li>Runs Quarto to produce Markdown and HTML versions.</li>
<li>Returns the file path to the Markdown document.</li>
</ul></li>
<li><strong>Analyse our Markdown slides with the LLM (we call this task<code>chat_task</code>)</strong>
<ul>
<li>Waits for the Markdown to be ready, so it can read the content of the Markdown file using the file path.</li>
<li>Starts a chat session with the model.</li>
<li>Runs two subtasks:
<ul>
<li>A “regular” async chat where the LLM can use our tool to count slides and calculate percentages.</li>
<li>A structured async chat that returns clean, structured data according to our data model.</li>
</ul></li>
</ul></li>
</ol>
<p>Because the LLM depends on the Markdown output, we have to <strong>chain these tasks</strong>: the button click first kicks off <code>quarto_task</code>, and only when that’s finished, we run <code>chat_task</code>.</p>
<p>This chaining is done through Shiny’s reactive system:</p>
<ul>
<li>A reactive effect (<code>run_quarto</code> in Python) / observer (R) responds to the button press and invokes our first extended task: <code>quarto_task</code>.</li>
<li>Another reactive effect (<code>run_chat</code> in Python) / observer (R) listens for <code>quarto_task</code> to finish, then reads the rendered Markdown using the file path and invokes our second extended task: <code>chat_task</code>.</li>
<li><code>analysis_result</code> is a reactive that listens for <code>chat_task</code> to complete and prepares the final output for the UI.</li>
</ul>
<p>You can think of it as a pipeline:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-pipeline.png" class="img-fluid"></p>
<p>This setup keeps the app responsive, ensures tasks run in the right order, and makes the logic clear: the button starts things off, the tasks are executed async (but in order!), and our desired data ends up in a reactive that we can use as information source for all the UI components.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
The world of async programming
</div>
</div>
<div class="callout-body-container callout-body">
<p>If you’re used to synchronous code and just running scripts, using asynchronous tasks might feel a bit… complicated? Confusing, perhaps? Ultimately, it requires you to think differently. If you’re keen to learn more, and getting hands on with a bunch of different examples, you can check out the <a href="https://shiny.posit.co/py/docs/nonblocking.html">Python</a> or <a href="https://shiny.posit.co/r/articles/improve/nonblocking/">R</a> docs about non-blocking operations. The <a href="https://github.com/hypebright/async_shiny/tree/main">async_shiny</a> repo also contains examples.</p>
</div>
</div>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
A note on code snippets
</div>
</div>
<div class="callout-body-container callout-body">
<p>Big chunks of code are generally not nice to look at. So, to make sure it’s not too overwhelming we’ll take a look at some snippets from the finalised DeckCheck app. Note that you can’t run these snippets on their own. If you want to run the full DeckCheck application you can head over to the the end result.</p>
</div>
</div>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-5-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-1" aria-controls="tabset-5-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-5-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-2" aria-controls="tabset-5-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-5-1" class="tab-pane active" aria-labelledby="tabset-5-1-tab">
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb11-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Data Structure</span></span>
<span id="cb11-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb11-4">ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)]</span>
<span id="cb11-5">PercentType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100.0</span>)]</span>
<span id="cb11-6">MinutesType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb11-7">SlideCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb11-8"></span>
<span id="cb11-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> ScoringCategory(BaseModel):</span>
<span id="cb11-10">    score: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1–10."</span>)</span>
<span id="cb11-11">    justification: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span>)</span>
<span id="cb11-12">    improvements: Optional[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb11-13">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>,</span>
<span id="cb11-14">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb11-15">    )</span>
<span id="cb11-16">    score_after_improvements: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb11-17">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb11-18">    )</span>
<span id="cb11-19"></span>
<span id="cb11-20"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> DeckAnalysis(BaseModel):</span>
<span id="cb11-21">    presentation_title: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>)</span>
<span id="cb11-22">    total_slides: SlideCount</span>
<span id="cb11-23">    percent_with_code: PercentType</span>
<span id="cb11-24">    percent_with_images: PercentType</span>
<span id="cb11-25">    estimated_duration_minutes: MinutesType</span>
<span id="cb11-26">    tone: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb11-27">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the tone of the presentation."</span></span>
<span id="cb11-28">    )</span>
<span id="cb11-29"></span>
<span id="cb11-30">    clarity: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb11-31">        ...,</span>
<span id="cb11-32">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb11-33">    )</span>
<span id="cb11-34">    relevance: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb11-35">        ...,</span>
<span id="cb11-36">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Assess how well the content matches the audience's background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb11-37">    )</span>
<span id="cb11-38">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Truncated for brevity</span></span>
<span id="cb11-39">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb11-40">    </span>
<span id="cb11-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb11-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny App</span></span>
<span id="cb11-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb11-44">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fillable(</span>
<span id="cb11-45">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## Our UI</span></span>
<span id="cb11-46">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb11-47">    ui.input_task_button(</span>
<span id="cb11-48">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>,</span>
<span id="cb11-49">        icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(robot),</span>
<span id="cb11-50">        label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyse presentation"</span>,</span>
<span id="cb11-51">    )</span>
<span id="cb11-52">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb11-53">)</span>
<span id="cb11-54"></span>
<span id="cb11-55"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb11-56">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@ui.bind_task_button</span>(button_id<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb11-57">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.extended_task</span></span>
<span id="cb11-58">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> quarto_task(file_path, temp_dir):</span>
<span id="cb11-59">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an Extended Task to avoid blocking. Note that</span></span>
<span id="cb11-60">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a temporary directory called within mirai will be</span></span>
<span id="cb11-61">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># different from the one in the "main" Shiny session. Hence,</span></span>
<span id="cb11-62">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we pass a temp_dir parameter to the task and use that.</span></span>
<span id="cb11-63">        qmd_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(temp_dir) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.qmd"</span></span>
<span id="cb11-64">        shutil.copy(file_path, qmd_file)</span>
<span id="cb11-65"></span>
<span id="cb11-66">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Run asyncio subprocess</span></span>
<span id="cb11-67">        proc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> asyncio.create_subprocess_exec(</span>
<span id="cb11-68">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(qmd_file), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown,html"</span></span>
<span id="cb11-69">        )</span>
<span id="cb11-70">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> proc.communicate()</span>
<span id="cb11-71"></span>
<span id="cb11-72">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return the path to the markdown file</span></span>
<span id="cb11-73">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> Path(temp_dir) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.md"</span></span>
<span id="cb11-74"></span>
<span id="cb11-75">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@ui.bind_task_button</span>(button_id<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb11-76">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.extended_task</span></span>
<span id="cb11-77">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> chat_task(system_prompt, markdown_content, DeckAnalysis):</span>
<span id="cb11-78">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an extended task to avoid blocking the session and</span></span>
<span id="cb11-79">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we start a fresh chat session each time.</span></span>
<span id="cb11-80">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># For a feedback loop, we would use a persistent chat session.</span></span>
<span id="cb11-81">        chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb11-82">            model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb11-83">            system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb11-84">        )</span>
<span id="cb11-85"></span>
<span id="cb11-86">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set model parameters (optional)</span></span>
<span id="cb11-87">        chat.set_model_params(</span>
<span id="cb11-88">            temperature<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb11-89">        )</span>
<span id="cb11-90"></span>
<span id="cb11-91">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb11-92">        chat.register_tool(calculate_slide_metric)</span>
<span id="cb11-93"></span>
<span id="cb11-94">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb11-95">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb11-96">        chat_res1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.chat_async(</span>
<span id="cb11-97">            interpolate(</span>
<span id="cb11-98">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb11-99">            )</span>
<span id="cb11-100">        )</span>
<span id="cb11-101"></span>
<span id="cb11-102">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(chat_res1)</span>
<span id="cb11-103"></span>
<span id="cb11-104">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb11-105">        chat_res2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.chat_structured_async(</span>
<span id="cb11-106">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb11-107">            data_model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>DeckAnalysis,</span>
<span id="cb11-108">        )</span>
<span id="cb11-109"></span>
<span id="cb11-110">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> chat_res2</span>
<span id="cb11-111"></span>
<span id="cb11-112">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.effect</span></span>
<span id="cb11-113">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.event</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.submit)</span>
<span id="cb11-114">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> run_quarto():</span>
<span id="cb11-115">        req(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb11-116"></span>
<span id="cb11-117">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get file path of the uploaded file</span></span>
<span id="cb11-118">        file_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"datapath"</span>]</span>
<span id="cb11-119"></span>
<span id="cb11-120">        quarto_task.invoke(file_path, tempfile.gettempdir())</span>
<span id="cb11-121"></span>
<span id="cb11-122">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.effect</span></span>
<span id="cb11-123">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> run_chat():</span>
<span id="cb11-124">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># require quarto_task result to be available</span></span>
<span id="cb11-125">        req(quarto_task.result() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb11-126">        </span>
<span id="cb11-127">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get the Markdown file path from the complete quarto_task</span></span>
<span id="cb11-128">        markdown_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> quarto_task.result()</span>
<span id="cb11-129">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing the slides</span></span>
<span id="cb11-130">        markdown_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> markdown_file.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb11-131"></span>
<span id="cb11-132">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb11-133">        system_prompt_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb11-134">            ROOT_DIR <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompts"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompt-analyse-slides-structured-tool.md"</span></span>
<span id="cb11-135">        )</span>
<span id="cb11-136"></span>
<span id="cb11-137">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb11-138">        system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb11-139">            system_prompt_file,</span>
<span id="cb11-140">            variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb11-141">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.audience(),</span>
<span id="cb11-142">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.length(),</span>
<span id="cb11-143">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">type</span>(),</span>
<span id="cb11-144">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.event(),</span>
<span id="cb11-145">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown_content"</span>: markdown_content,</span>
<span id="cb11-146">            },</span>
<span id="cb11-147">        )</span>
<span id="cb11-148"></span>
<span id="cb11-149">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trigger the chat task with the provided inputs</span></span>
<span id="cb11-150">        chat_task.invoke(system_prompt, markdown_content, DeckAnalysis)</span>
<span id="cb11-151"></span>
<span id="cb11-152">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># This reactive will be used as information source for</span></span>
<span id="cb11-153">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># all the UI elements. It uses the result from the chat_task,</span></span>
<span id="cb11-154">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># and does some data wrangling</span></span>
<span id="cb11-155">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.calc</span></span>
<span id="cb11-156">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> analysis_result():</span>
<span id="cb11-157">        res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chat_task.result()</span>
<span id="cb11-158">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>:</span>
<span id="cb11-159">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Some data wrangling</span></span>
<span id="cb11-160">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb11-161">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> res</span>
<span id="cb11-162">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb11-163">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb11-164"></span>
<span id="cb11-165">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
<p>Note that <code>quarto_task</code> is marked <code>async</code>, so it automatically returns a coroutine. You need a coroutine for <code>extended_task</code> to work. The subprocess call to Quarto runs without blocking (<code>await asyncio.create_subprocess_exec</code>). Once Quarto finishes, the task resolves with the path to the Markdown file. The async chat tasks from <code>chatlas</code> return a coroutine too.</p>
</div>
<div id="tabset-5-2" class="tab-pane" aria-labelledby="tabset-5-2-tab">
<div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb12-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb12-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Data Structure</span></span>
<span id="cb12-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb12-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reusable scoring category</span></span>
<span id="cb12-5">type_scoring_category <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb12-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb12-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1 to 10."</span></span>
<span id="cb12-8">  ),</span>
<span id="cb12-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">justification =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb12-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span></span>
<span id="cb12-11">  ),</span>
<span id="cb12-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb12-13">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb12-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb12-15">  ),</span>
<span id="cb12-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score_after_improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb12-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb12-18">  )</span>
<span id="cb12-19">)</span>
<span id="cb12-20"></span>
<span id="cb12-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Top-level deck analysis object</span></span>
<span id="cb12-22">type_deck_analysis <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb12-23">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">presentation_title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>),</span>
<span id="cb12-24">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_slides =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total number of slides."</span>),</span>
<span id="cb12-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb12-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing code blocks (0–100)."</span></span>
<span id="cb12-27">  ),</span>
<span id="cb12-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_images =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb12-29">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing images (0–100)."</span></span>
<span id="cb12-30">  ),</span>
<span id="cb12-31">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">estimated_duration_minutes =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb12-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated presentation length in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide."</span></span>
<span id="cb12-33">  ),</span>
<span id="cb12-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tone =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb12-35">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the presentation tone (e.g., informal, technical, playful)."</span></span>
<span id="cb12-36">  ),</span>
<span id="cb12-37">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">clarity =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb12-38">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb12-39">    type_scoring_category</span>
<span id="cb12-40">  ),</span>
<span id="cb12-41">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">relevance =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb12-42">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Asses how well the content matches the audience's background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb12-43">    type_scoring_category</span>
<span id="cb12-44">  ),</span>
<span id="cb12-45">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Truncated for brevity</span></span>
<span id="cb12-46">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb12-47">)</span>
<span id="cb12-48"></span>
<span id="cb12-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb12-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny App</span></span>
<span id="cb12-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb12-52">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fillable</span>(</span>
<span id="cb12-53">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Our UI</span></span>
<span id="cb12-54">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb12-55">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">input_task_button</span>(</span>
<span id="cb12-56">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">id =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>,</span>
<span id="cb12-57">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> shiny<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tagList</span>(</span>
<span id="cb12-58">      bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robot"</span>),</span>
<span id="cb12-59">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyse presentation"</span></span>
<span id="cb12-60">    ),</span>
<span id="cb12-61">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label_busy =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DeckCheck is checking..."</span>,</span>
<span id="cb12-62">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"default"</span></span>
<span id="cb12-63">  )</span>
<span id="cb12-64">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb12-65">)</span>
<span id="cb12-66"></span>
<span id="cb12-67">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb12-68">  quarto_task <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ExtendedTask<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(file_path, temp_dir) {</span>
<span id="cb12-69">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an Extended Task to avoid blocking. Note that</span></span>
<span id="cb12-70">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a temporary directory called within mirai will be</span></span>
<span id="cb12-71">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># different from the one in the "main" Shiny session. Hence,</span></span>
<span id="cb12-72">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we pass a temp_dir parameter to the task and use that.</span></span>
<span id="cb12-73">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mirai</span>(</span>
<span id="cb12-74">      {</span>
<span id="cb12-75">        qmd_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.path</span>(temp_dir, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.qmd"</span>)</span>
<span id="cb12-76">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.copy</span>(file_path, qmd_file, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">overwrite =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb12-77"></span>
<span id="cb12-78">        quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb12-79">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input =</span> qmd_file,</span>
<span id="cb12-80">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"html"</span>)</span>
<span id="cb12-81">        )</span>
<span id="cb12-82"></span>
<span id="cb12-83">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return the path to the markdown file</span></span>
<span id="cb12-84">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.path</span>(temp_dir, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.md"</span>)</span>
<span id="cb12-85">      },</span>
<span id="cb12-86">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Use the same environment as the Shiny app</span></span>
<span id="cb12-87">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">environment</span>()</span>
<span id="cb12-88">    )</span>
<span id="cb12-89">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb12-90">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bind_task_button</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb12-91"></span>
<span id="cb12-92">  chat_task <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ExtendedTask<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(</span>
<span id="cb12-93">    system_prompt,</span>
<span id="cb12-94">    markdown_content,</span>
<span id="cb12-95">    type_deck_analysis</span>
<span id="cb12-96">  ) {</span>
<span id="cb12-97">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an Extended Task to avoid blocking the session and</span></span>
<span id="cb12-98">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we start a fresh chat session each time.</span></span>
<span id="cb12-99">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># For a feedback loop, we would use a persistent chat session.</span></span>
<span id="cb12-100">    chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb12-101">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb12-102">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt,</span>
<span id="cb12-103">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span>(</span>
<span id="cb12-104">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temperature =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb12-105">      )</span>
<span id="cb12-106">    )</span>
<span id="cb12-107"></span>
<span id="cb12-108">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb12-109">    chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(calculate_slide_metric)</span>
<span id="cb12-110"></span>
<span id="cb12-111">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb12-112">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb12-113">    chat_res <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_async</span>(</span>
<span id="cb12-114">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb12-115">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb12-116">      )</span>
<span id="cb12-117">    )</span>
<span id="cb12-118"></span>
<span id="cb12-119">    chat_res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(res) {</span>
<span id="cb12-120">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print the response from Task 1</span></span>
<span id="cb12-121">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Response from Task 1:</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-122">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(res, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb12-123"></span>
<span id="cb12-124">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Execute next task</span></span>
<span id="cb12-125">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb12-126">      chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_structured_async</span>(</span>
<span id="cb12-127">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb12-128">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_deck_analysis</span>
<span id="cb12-129">      )</span>
<span id="cb12-130">    })</span>
<span id="cb12-131">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb12-132">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bind_task_button</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb12-133"></span>
<span id="cb12-134">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb12-135">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>file)</span>
<span id="cb12-136">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>audience)</span>
<span id="cb12-137">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>length)</span>
<span id="cb12-138">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>type)</span>
<span id="cb12-139">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>event)</span>
<span id="cb12-140"></span>
<span id="cb12-141">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get file path of the uploaded file</span></span>
<span id="cb12-142">    file_path <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>file<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>datapath</span>
<span id="cb12-143"></span>
<span id="cb12-144">    quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invoke</span>(file_path, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temp_dir =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tempdir</span>())</span>
<span id="cb12-145">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb12-146">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bindEvent</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>submit)</span>
<span id="cb12-147"></span>
<span id="cb12-148">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb12-149">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">result</span>())</span>
<span id="cb12-150">    </span>
<span id="cb12-151">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get the Markdown file path from the completed quarto_task</span></span>
<span id="cb12-152">    markdown_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">result</span>()</span>
<span id="cb12-153">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing the slides</span></span>
<span id="cb12-154">    markdown_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(markdown_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(markdown_file))</span>
<span id="cb12-155"></span>
<span id="cb12-156">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb12-157">    system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> here<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">here</span>(</span>
<span id="cb12-158">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompts"</span>,</span>
<span id="cb12-159">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompt-analyse-slides-structured-tool.md"</span></span>
<span id="cb12-160">    )</span>
<span id="cb12-161"></span>
<span id="cb12-162">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb12-163">    system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb12-164">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb12-165">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>audience,</span>
<span id="cb12-166">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>length,</span>
<span id="cb12-167">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>type,</span>
<span id="cb12-168">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>event</span>
<span id="cb12-169">    )</span>
<span id="cb12-170"></span>
<span id="cb12-171">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trigger the chat task with the provided inputs</span></span>
<span id="cb12-172">    chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invoke</span>(</span>
<span id="cb12-173">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt,</span>
<span id="cb12-174">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">markdown_content =</span> markdown_content,</span>
<span id="cb12-175">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type_deck_analysis =</span> type_deck_analysis</span>
<span id="cb12-176">    )</span>
<span id="cb12-177">  })</span>
<span id="cb12-178"></span>
<span id="cb12-179">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># This reactive will be used as information source for</span></span>
<span id="cb12-180">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># all the UI elements. It uses the result from the chat_task,</span></span>
<span id="cb12-181">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># and does some data wrangling</span></span>
<span id="cb12-182">  analysis_result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reactive</span>({</span>
<span id="cb12-183">    named_list <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">result</span>()</span>
<span id="cb12-184"></span>
<span id="cb12-185">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Some data wrangling</span></span>
<span id="cb12-186">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb12-187">        </span>
<span id="cb12-188">    named_list</span>
<span id="cb12-189">  })</span>
<span id="cb12-190"></span>
<span id="cb12-191"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
<p>Note that we wrap the Quarto task in <code>mirai</code> because we need a promise. Basically an object that says: “I don’t have the answer yet, but I will later”. <code>ExtendedTask</code> is built to work with promises and it expects whatever you give it to eventually resolve with a value. The async chat tasks from <code>ellmer</code> return a promise too.</p>
</div>
</div>
</div>
<p>In this case, we choose to start a fresh chat session each time. Another way to do this would be to bring up the code that initialises the chat client, set model parameters, and registers our tool so it only runs once at the start of the session. Then, we could use <a href="https://posit-dev.github.io/chatlas/reference/Chat.html#chatlas.Chat.set_turns"><code>chat.set_turns([])</code></a> (Python) / <a href="https://ellmer.tidyverse.org/reference/Chat.html#method-set-turns-"><code>chat$set_turns([])</code></a> (R) before each new “chat task”. This way, we won’t accumulate chat history.</p>
</section>
<section id="towards-a-better-ui" class="level1">
<h1>Towards a better UI</h1>
<p>There’s an engine, and there’s a basic UI. Time to put them together. But if we want a Shiny app to truly shine, we need to give the UI a little extra love. In this section, we’ll go over all the UI elements that we have in our design, plus some extra goodies. The suggestions here are just examples though, the possibilities go far beyond what we’ll cover here.</p>
<section id="loading-experience" class="level2">
<h2 class="anchored" data-anchor-id="loading-experience">Loading experience</h2>
<p>The fact that we’re executing tasks asynchronously doesn’t change anything about the execution speed of those tasks. It still takes time to render Quarto, and it still takes time to get a response from the LLM. And since we do need to finish one task before starting the other, there’s zero time gain compared to running the code synchronously for <strong>individual users</strong>. We simply made the app non-blocking, which is especially valuable if there are multiple users running the app at the same time (concurrently).</p>
<p>We already talked about the impatience of users and how visual feedback can make waiting a bit more enjoyable. So, in DeckCheck, we also need to make sure to provide such feedback. We want it to be informative (e.g.&nbsp;“Processing your Quarto presentation…” or “The LLM is doing its magic…”) and we want it to be entertaining (bouncing robot anyone?!).</p>
<p>A nice way to add such a custom loading experience is with the help of <a href="https://shiny.posit.co/py/api/core/ui.output_ui.html"><code>output_ui</code></a> (Python) / <a href="https://shiny.posit.co/r/reference/shiny/latest/renderui.html"><code>uiOutput</code></a> (R). It serves as a placeholder in your UI that gets filled later with server-generated UI via <code>render.ui()</code> (Python) / <code>renderUI()</code> (R). This lets you create dynamic interfaces that change depending on app state. For example, we can start by rendering a loading animation (like a bouncing icon), and once the results are ready, replace it with more complex UI elements such as value boxes, a graph, and a table.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-6-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-1" aria-controls="tabset-6-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-6-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-2" aria-controls="tabset-6-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-6-1" class="tab-pane active" aria-labelledby="tabset-6-1-tab">
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb13-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny App</span></span>
<span id="cb13-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb13-4">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fillable(</span>
<span id="cb13-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb13-6">    ui.layout_sidebar(</span>
<span id="cb13-7">        ui.sidebar(</span>
<span id="cb13-8">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Sidebar content</span></span>
<span id="cb13-9">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb13-10">        ),</span>
<span id="cb13-11">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Main content is now dynamic</span></span>
<span id="cb13-12">        ui.output_ui(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results"</span>),</span>
<span id="cb13-13">    ),</span>
<span id="cb13-14">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb13-15">)</span>
<span id="cb13-16"></span>
<span id="cb13-17"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb13-18">    </span>
<span id="cb13-19">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb13-20"></span>
<span id="cb13-21">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.ui</span></span>
<span id="cb13-22">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> results():</span>
<span id="cb13-23">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> quarto_task.status() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>:</span>
<span id="cb13-24">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.div(</span>
<span id="cb13-25">                ui.HTML(file_slides_loader),</span>
<span id="cb13-26">                ui.br(),</span>
<span id="cb13-27">                ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Processing your Quarto presentation..."</span>),</span>
<span id="cb13-28">                class_<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb13-29">                style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%"</span>,</span>
<span id="cb13-30">            )</span>
<span id="cb13-31"></span>
<span id="cb13-32">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> chat_task.status() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>:</span>
<span id="cb13-33">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.div(</span>
<span id="cb13-34">                ui.HTML(robot_loader),</span>
<span id="cb13-35">                ui.br(),</span>
<span id="cb13-36">                ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The LLM is doing its magic..."</span>),</span>
<span id="cb13-37">                class_<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb13-38">                style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%"</span>,</span>
<span id="cb13-39">            )</span>
<span id="cb13-40"></span>
<span id="cb13-41">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> chat_task.status() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"success"</span>:</span>
<span id="cb13-42">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.TagList(</span>
<span id="cb13-43">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Final results UI components</span></span>
<span id="cb13-44">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb13-45">            )</span>
<span id="cb13-46"></span>
<span id="cb13-47">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
</div>
<div id="tabset-6-2" class="tab-pane" aria-labelledby="tabset-6-2-tab">
<div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb14-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny App</span></span>
<span id="cb14-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb14-4">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fillable</span>(</span>
<span id="cb14-5">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb14-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layout_sidebar</span>(</span>
<span id="cb14-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Sidebar content</span></span>
<span id="cb14-8">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb14-9">    ),</span>
<span id="cb14-10">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Main content is now dynamic</span></span>
<span id="cb14-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uiOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"100%"</span>)</span>
<span id="cb14-12">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb14-13">  )</span>
<span id="cb14-14"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb14-15"></span>
<span id="cb14-16">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb14-17">  </span>
<span id="cb14-18">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb14-19"></span>
<span id="cb14-20">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>results <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderUI</span>({</span>
<span id="cb14-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">status</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>) {</span>
<span id="cb14-22">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">div</span>(</span>
<span id="cb14-23">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb14-24">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%;"</span>,</span>
<span id="cb14-25">        bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(</span>
<span id="cb14-26">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-slides"</span>,</span>
<span id="cb14-27">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"6em"</span>,</span>
<span id="cb14-28">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-primary bounce"</span></span>
<span id="cb14-29">        ),</span>
<span id="cb14-30">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">br</span>(),</span>
<span id="cb14-31">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Processing your Quarto presentation..."</span>)</span>
<span id="cb14-32">      )</span>
<span id="cb14-33">    } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">status</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>) {</span>
<span id="cb14-34">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">div</span>(</span>
<span id="cb14-35">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb14-36">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%;"</span>,</span>
<span id="cb14-37">        bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(</span>
<span id="cb14-38">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robot"</span>,</span>
<span id="cb14-39">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"6em"</span>,</span>
<span id="cb14-40">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-primary bounce"</span></span>
<span id="cb14-41">        ),</span>
<span id="cb14-42">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">br</span>(),</span>
<span id="cb14-43">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The LLM is doing its magic..."</span>)</span>
<span id="cb14-44">      )</span>
<span id="cb14-45">    } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">status</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"success"</span>) {</span>
<span id="cb14-46">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tagList</span>(</span>
<span id="cb14-47">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Final results UI components</span></span>
<span id="cb14-48">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb14-49">      )</span>
<span id="cb14-50">    }</span>
<span id="cb14-51">  })</span>
<span id="cb14-52"></span>
<span id="cb14-53"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
</div>
</div>
</div>
<p>Using extended task, we can easily monitor the status with <code>quarto_task$status()</code> and <code>chat_task$status()</code>. The status can be <code>"initial"</code>, <code>"running"</code>, <code>"success"</code>, or <code>"error"</code>. So whenever our Quarto task is running, we can display a bouncing presentation easel (or whatever you like). And once that task is finished and we start with our chat task, we can show a bouncing robot. The HTML for those bouncing icons is pretty straightforward: a simple div that puts the icon in the middle. For the bounce effect we need some custom CSS that we can add with the <code>.bounce</code> class. It looks like this:</p>
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode css code-with-copy"><code class="sourceCode css"><span id="cb15-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.bounce</span> {</span>
<span id="cb15-2">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">animation</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">bounce 2</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">s</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">infinite</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb15-3">}</span>
<span id="cb15-4"></span>
<span id="cb15-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">@keyframes</span> bounce {</span>
<span id="cb15-6">    <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span> {</span>
<span id="cb15-7">        <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">transform</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">translateY(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb15-8">    }</span>
<span id="cb15-9">    <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50%</span> {</span>
<span id="cb15-10">        <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">transform</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">translateY(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">-20</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">px</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb15-11">    }</span>
<span id="cb15-12">}</span></code></pre></div>
<p>We’ll look at how to add this custom CSS to your Shiny app a little later.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Built-in busy indicators
</div>
</div>
<div class="callout-body-container callout-body">
<p>By default, a page-level pulsing banner and a spinner will be shown on recalculating outputs like plots and tables. This means that whenever the app is busy with calculations (like getting the results from an LLM) there will be some visual feedback for the user. You can change the appearance and options of these busy indicators with <a href="https://shiny.posit.co/py/api/core/ui.busy_indicators.options.html#shiny.ui.busy_indicators.options">ui.busy_indicators.options</a> (Python) / <a href="https://shiny.posit.co/r/reference/shiny/latest/busyindicatoroptions.html">busyIndicatorOptions</a> (R). In Python, a spinner shows by default on <code>output_plot</code> and <code>output_data_frame</code>. In R, on <code>plotOutput</code> and <code>tableOutput</code>. Spinners won’t be shown on value boxes and HTML widgets, that’s why a spinner overlay like in our example works well.</p>
</div>
</div>
<p>What a lovely result 🤖:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-loading-ux.gif" class="img-fluid"></p>
</section>
<section id="value-boxes-with-tooltips" class="level2">
<h2 class="anchored" data-anchor-id="value-boxes-with-tooltips">Value boxes with tooltips</h2>
<p>With value boxes you can display key numbers. The idea is simple: show a number or short text, add an image, icon, or sparkline, and make sure the value updates whenever the underlying data changes.</p>
<p>Value boxes can be enhanced with tooltips. Tooltips are one of those small details that make a big difference. They’re perfect for adding extra information without cluttering up your interface. Think of them as extra context that appears when someone hovers or taps. You can use them to explain numbers or tricky terms, add short instructions, highlight what a button actually does, or even drop in a quick example. In our case, we could add tooltips to the value boxes to tell our users how the numbers were calculated.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-7-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-1" aria-controls="tabset-7-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-7-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-2" aria-controls="tabset-7-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-7-1" class="tab-pane active" aria-labelledby="tabset-7-1-tab">
<p>UI:</p>
<div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># SVG icon copied from https://icons.getbootstrap.com/icons/file-slides-fill/</span></span>
<span id="cb16-2">file_slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-slides-fill" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb16-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M7 7.78V5.22c0-.096.106-.156.19-.106l2.13 1.279a.125.125 0 0 1 0 .214l-2.13 1.28A.125.125 0 0 1 7 7.778z"/&gt;</span></span>
<span id="cb16-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M12 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M5 4h6a.5.5 0 0 1 .496.438l.5 4A.5.5 0 0 1 11.5 9h-3v2.016c.863.055 1.5.251 1.5.484 0 .276-.895.5-2 .5s-2-.224-2-.5c0-.233.637-.429 1.5-.484V9h-3a.5.5 0 0 1-.496-.562l.5-4A.5.5 0 0 1 5 4"/&gt;</span></span>
<span id="cb16-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb16-6"></span>
<span id="cb16-7">ui.tooltip(</span>
<span id="cb16-8">    ui.value_box(</span>
<span id="cb16-9">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Showtime"</span>,</span>
<span id="cb16-10">        ui.output_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"showtime"</span>),</span>
<span id="cb16-11">        showcase<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(file_slides),</span>
<span id="cb16-12">        theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span>,</span>
<span id="cb16-13">    ),</span>
<span id="cb16-14">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Slides are being counted based on the provided Quarto presentation, then an educated guess is made about the time it will take to present them."</span>,</span>
<span id="cb16-15">)</span></code></pre></div>
<p>Server:</p>
<div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb17-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> showtime():</span>
<span id="cb17-3">    res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb17-4"></span>
<span id="cb17-5">    req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb17-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'meta'</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'estimated_duration_minutes'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> minutes"</span></span></code></pre></div>
<p>In this case, the value contains some text (the length of the presentation in minutes) and a showcase (an icon), but use your creativity to build anything you want! You can find some fun examples <a href="https://shiny.posit.co/py/components/outputs/value-box/">here</a>.</p>
<p>You can add a <a href="https://shiny.posit.co/py/components/display-messages/tooltips/">tooltip</a> to any UI element with <code>ui.tooltip()</code>. In this case, the tooltip is applied to the complete value box.</p>
<p>The result:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-tooltips-py.gif" class="img-fluid"></p>
</div>
<div id="tabset-7-2" class="tab-pane" aria-labelledby="tabset-7-2-tab">
<p>UI:</p>
<div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb18-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">value_box</span>(</span>
<span id="cb18-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tooltip</span>(</span>
<span id="cb18-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">span</span>(</span>
<span id="cb18-4">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Showtime "</span>,</span>
<span id="cb18-5">      bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"question-circle-fill"</span>)</span>
<span id="cb18-6">    ),</span>
<span id="cb18-7">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Slides are being counted based on the provided Quarto presentation, then an educated guess is made about the time it will take to present them."</span></span>
<span id="cb18-8">  ),</span>
<span id="cb18-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"showtime"</span>),</span>
<span id="cb18-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">showcase =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-slides"</span>),</span>
<span id="cb18-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span></span>
<span id="cb18-12">)</span></code></pre></div>
<p>Server:</p>
<div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb19-1">output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>showtime <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderText</span>({</span>
<span id="cb19-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb19-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb19-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>meta<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>estimated_duration_minutes,</span>
<span id="cb19-5">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" minutes"</span></span>
<span id="cb19-6">  )</span>
<span id="cb19-7">})</span></code></pre></div>
<p>Looking for more fun examples? Take a look <a href="https://shiny.posit.co/r/components/outputs/value-box/">here</a>.</p>
<p>You can add a tooltip to any UI element with <code>tooltip()</code>. In this case, the tooltip is applied to a little info icon (generated with <code>span</code> and <code>bs_icon</code>). Alternatively, you could also apply it to the complete value box.</p>
<p>The result:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-tooltips-r.gif" class="img-fluid"></p>
</div>
</div>
</div>
</section>
<section id="interactive-plots" class="level2">
<h2 class="anchored" data-anchor-id="interactive-plots">Interactive plots</h2>
<p>Is it even a data-powered app if there isn’t a good plot?!</p>
<p>There are plenty of options when it comes to visualisation libraries, and many of them support interactivity as well. With Shiny you can easily add interactive plots, and you can even link events (clicking, brushing, hovering) to other parts of your app. For example, adding click and brushing would let users dig deeper into the results. Clicking on a bar in our score graph could filter the table down to just that category’s feedback, while brushing across multiple bars would make it possible to look at several categories side by side. And lets not forget about hovering: users expect to see more information when they hover over a score. You can control click, brush, and hover events with <a href="https://shiny.posit.co/py/api/core/ui.output_plot.html"><code>output_plot</code></a> (Python) / <a href="https://shiny.posit.co/r/reference/shiny/latest/plotoutput.html"><code>plotOutput</code></a> (R). This makes it easy to send values back to the server. To give an example: you can show a modal when someone clicks on a bar.</p>
<p>To keep the demo light, we’re not going to focus too much on these events. But there’s one thing that we can add very easily and is supported by most interactive visualisation libraries: tooltips on hover! When you hover over a bar (or a line, or a point, you get it), you’ll see a quick popup with more information. In our case: the score and the justification. Even this small touch already makes the chart feel more alive, and it’s easy to imagine how combining clicks, brushing, and tooltips could make DeckCheck even cooler.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-8-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-1" aria-controls="tabset-8-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-8-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-2" aria-controls="tabset-8-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-8-1" class="tab-pane active" aria-labelledby="tabset-8-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>Want to create plots based on the grammar of graphics? Take a look at <a href="https://plotnine.org">plotnine</a>! Unfortunately not interactive (yet), but you can make really pretty figures that would fit nicely into an app.</p>
</div>
</div>
<p>In Python, you have plenty of options for interactive plots, like <a href="https://shiny.posit.co/py/components/outputs/plot-plotly">Plotly</a> or <a href="https://altair-viz.github.io">Altair</a>. The choice is yours! Our weapon of choice for DeckCheck: Plotly.</p>
<p>To make a Plotly plot interactive, you need <a href="https://github.com/posit-dev/py-shinywidgets"><code>shinywidgets</code></a>. More specifically: <code>output_widget</code> on the UI side, and <code>render_widget</code> on the server side. It connects Shiny with ipywidgets, letting your sliders, dropdowns, and buttons actually control the plot.</p>
<p>UI:</p>
<div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb20-1">ui.card(</span>
<span id="cb20-2">    ui.card_header(ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Scores per category"</span>)),</span>
<span id="cb20-3">    output_widget(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scores"</span>),</span>
<span id="cb20-4">    height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"600px"</span>,</span>
<span id="cb20-5">)</span></code></pre></div>
<p>Server:</p>
<div class="sourceCode" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render_widget</span></span>
<span id="cb21-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> scores():</span>
<span id="cb21-3">    res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb21-4"></span>
<span id="cb21-5">    req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb21-6"></span>
<span id="cb21-7">    evals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"evals"</span>].copy()</span>
<span id="cb21-8">    evals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals.sort_values(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>)</span>
<span id="cb21-9">    evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.Categorical(</span>
<span id="cb21-10">        evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>], categories<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>], ordered<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb21-11">    )</span>
<span id="cb21-12"></span>
<span id="cb21-13">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># apply to the justification column</span></span>
<span id="cb21-14">    evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification_wrapped"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification"</span>].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">apply</span>(add_line_breaks)</span>
<span id="cb21-15"></span>
<span id="cb21-16">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create a custom tooltip column</span></span>
<span id="cb21-17">    evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tooltip"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb21-18">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score: "</span></span>
<span id="cb21-19">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>].astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>)</span>
<span id="cb21-20">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;br&gt;After improvements: "</span></span>
<span id="cb21-21">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>].astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>)</span>
<span id="cb21-22">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;br&gt;Justification: "</span></span>
<span id="cb21-23">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification_wrapped"</span>]</span>
<span id="cb21-24">    )</span>
<span id="cb21-25"></span>
<span id="cb21-26">    plot <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> px.bar(</span>
<span id="cb21-27">        evals,</span>
<span id="cb21-28">        x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>,</span>
<span id="cb21-29">        y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>,</span>
<span id="cb21-30">        orientation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"h"</span>,</span>
<span id="cb21-31">        labels<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Category"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score"</span>},</span>
<span id="cb21-32">        hover_data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tooltip"</span>: <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>},  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># include the tooltip column</span></span>
<span id="cb21-33">    )</span>
<span id="cb21-34"></span>
<span id="cb21-35">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set hovertemplate to use our custom tooltip</span></span>
<span id="cb21-36">    plot.update_traces(</span>
<span id="cb21-37">        hovertemplate<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{customdata[0]}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;extra&gt;&lt;/extra&gt;"</span>,</span>
<span id="cb21-38">        customdata<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tooltip"</span>]].values,</span>
<span id="cb21-39">    )</span>
<span id="cb21-40"></span>
<span id="cb21-41">    plot.update_traces(marker_color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#18bc9c"</span>)</span>
<span id="cb21-42"></span>
<span id="cb21-43">    plot.update_layout(template<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"simple_white"</span>)</span>
<span id="cb21-44"></span>
<span id="cb21-45">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> plot</span></code></pre></div>
<p>To construct the tooltip we create a little helper to make sure there are line breaks. Otherwise the tooltip runs of the screen!</p>
<div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb22-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb22-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Tooltip helper</span></span>
<span id="cb22-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb22-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> add_line_breaks(text, width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>):</span>
<span id="cb22-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">isinstance</span>(text, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb22-6">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> text</span>
<span id="cb22-7"></span>
<span id="cb22-8">    words <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> text.split()</span>
<span id="cb22-9">    lines <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb22-10">    current_line <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span></span>
<span id="cb22-11"></span>
<span id="cb22-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> word <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> words:</span>
<span id="cb22-13">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># +1 accounts for the space if current_line isn't empty</span></span>
<span id="cb22-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(current_line) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(word) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> current_line <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> width:</span>
<span id="cb22-15">            current_line <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" "</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> current_line <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> word</span>
<span id="cb22-16">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb22-17">            lines.append(current_line)</span>
<span id="cb22-18">            current_line <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> word</span>
<span id="cb22-19"></span>
<span id="cb22-20">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> current_line:</span>
<span id="cb22-21">        lines.append(current_line)</span>
<span id="cb22-22"></span>
<span id="cb22-23">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;br&gt;"</span>.join(lines)</span></code></pre></div>
<p>The result is simple, but effective:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-plot-py.gif" class="img-fluid"></p>
</div>
<div id="tabset-8-2" class="tab-pane" aria-labelledby="tabset-8-2-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Cross-widget interactions
</div>
</div>
<div class="callout-body-container callout-body">
<p>Interested in cross-widget interactions like linked brushing and filtering? Take a look at <a href="https://rstudio.github.io/crosstalk/shiny.html">crosstalk</a>.</p>
</div>
</div>
<p>Where would we be without our beloved <code>ggplot2</code>… So of course it would be nice if we can could make our <code>ggplot2</code> interactive. The solution: <a href="https://davidgohel.github.io/ggiraph/"><code>ggiraph</code></a>! With <code>ggiraph</code> you can simply add tooltips, hover effects, and JavaScript actions to your plots. There’s plenty of options to alter the look and feel of the interactive elements, so you can be very creative. If you’re looking for more inspiration with <code>ggiraph</code>, check out <a href="https://posit.co/blog/shiny-dashboards-with-ggiraph-and-databases/">this post</a> by Isabella Velásquez.</p>
<p>We’ll stick to the basic with DeckCheck by adding <a href="https://davidgohel.github.io/ggiraph/reference/geom_bar_interactive.html"><code>geom_bar_interactive</code></a> and some tooltip options. To make it all work we need to use <code>girafeOutput</code> and <code>renderGirafe</code> (as opposed to <code>plotOutput</code> and <code>renderPlot</code>).</p>
<p>UI:</p>
<div class="sourceCode" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb23-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb23-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">600</span>,</span>
<span id="cb23-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(</span>
<span id="cb23-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Scores per category"</span>)</span>
<span id="cb23-5">  ),</span>
<span id="cb23-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">girafeOutput</span>(</span>
<span id="cb23-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">outputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scores"</span></span>
<span id="cb23-8">  )</span>
<span id="cb23-9">)</span></code></pre></div>
<p>Server:</p>
<div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb24-1">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>scores <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderGirafe</span>({</span>
<span id="cb24-2">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb24-3"></span>
<span id="cb24-4">    evals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>evals</span>
<span id="cb24-5"></span>
<span id="cb24-6">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Order by score</span></span>
<span id="cb24-7">    data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> evals <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb24-8">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(score) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb24-9">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb24-10">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(category, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">levels =</span> category),</span>
<span id="cb24-11">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tooltip =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb24-12">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score: "</span>,</span>
<span id="cb24-13">          score,</span>
<span id="cb24-14">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb24-15">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"After improvements: "</span>,</span>
<span id="cb24-16">          score_after_improvements,</span>
<span id="cb24-17">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb24-18">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Justification: "</span>,</span>
<span id="cb24-19">          justification</span>
<span id="cb24-20">        )</span>
<span id="cb24-21">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb24-22">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(category, score, score_after_improvements, tooltip)</span>
<span id="cb24-23"></span>
<span id="cb24-24">    p <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(</span>
<span id="cb24-25">      data,</span>
<span id="cb24-26">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> category, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> score, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tooltip =</span> tooltip, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data_id =</span> category)</span>
<span id="cb24-27">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb24-28">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_bar_interactive</span>(</span>
<span id="cb24-29">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stat =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"identity"</span>,</span>
<span id="cb24-30">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#18bc9c"</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Success color of Flatly theme</span></span>
<span id="cb24-31">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb24-32">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(</span>
<span id="cb24-33">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Category"</span>,</span>
<span id="cb24-34">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score"</span></span>
<span id="cb24-35">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb24-36">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flip to make horizontal bar chart</span></span>
<span id="cb24-37">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coord_flip</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb24-38">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_family =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Lato"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb24-39">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>)</span>
<span id="cb24-40"></span>
<span id="cb24-41">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">girafe</span>(</span>
<span id="cb24-42">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ggobj =</span> p,</span>
<span id="cb24-43">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">options =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb24-44">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_selection</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>),</span>
<span id="cb24-45">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_sizing</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rescale =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb24-46">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_tooltip</span>(</span>
<span id="cb24-47">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">css =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background-color: #f0f0f0; color: #333; padding: 5px; border-radius: 5px; width: 200px;"</span></span>
<span id="cb24-48">        ),</span>
<span id="cb24-49">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_hover</span>(</span>
<span id="cb24-50">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">css =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"."</span></span>
<span id="cb24-51">        ),</span>
<span id="cb24-52">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_hover_inv</span>(</span>
<span id="cb24-53">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">css =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"opacity: 0.5;"</span></span>
<span id="cb24-54">        )</span>
<span id="cb24-55">      )</span>
<span id="cb24-56">    )</span>
<span id="cb24-57">  })</span></code></pre></div>
<p>And that’s how you add interactivity to your <code>ggplot2</code> fast:</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-plot-r.gif" class="img-fluid"></p>
</div>
</div>
</div>
</section>
<section id="tables" class="level2">
<h2 class="anchored" data-anchor-id="tables">Tables</h2>
<p>Another element that you’ll see in web apps: tables. The good news is: displaying your data in Shiny is super easy. Got a pandas/polars/narwhals DataFrame in Python? Or a data.frame/tibble/data.table in R? You can drop it straight into <a href="https://shiny.posit.co/py/api/core/render.data_frame.html"><code>render.data_frame</code></a> / <a href="https://shiny.posit.co/py/api/core/ui.output_data_frame.html"><code>output_data_frame</code></a> (Python) or <a href="https://shiny.posit.co/r/reference/shiny/latest/rendertable.html"><code>renderTable</code></a> / <a href="https://shiny.posit.co/r/reference/shiny/latest/rendertable.html"><code>tableOutput</code></a> (R) (and many other similar functions for some variety).</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-9-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-9-1" aria-controls="tabset-9-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-9-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-9-2" aria-controls="tabset-9-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-9-1" class="tab-pane active" aria-labelledby="tabset-9-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Other ways to render your table
</div>
</div>
<div class="callout-body-container callout-body">
<p>You can also take a look at <a href="https://shiny.posit.co/py/api/core/render.DataGrid.html">render.DataGrid</a> (spreadsheet-like view), <a href="https://shiny.posit.co/py/api/core/render.table.html">render.table</a> (basic HTML table, not as nice as <a href="https://shiny.posit.co/py/api/core/render.data_frame.html">render.data_frame</a>) and <a href="https://shiny.posit.co/py/api/core/render.DataTable.html#shiny.render.DataTable">render.DataTable</a> (more tabular view of data).</p>
</div>
</div>
<p>UI:</p>
<div class="sourceCode" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1">ui.card(</span>
<span id="cb25-2">    ui.card_header(</span>
<span id="cb25-3">        ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Suggested improvements per category"</span>)</span>
<span id="cb25-4">    ),</span>
<span id="cb25-5">    ui.output_data_frame(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"suggested_improvements"</span>),</span>
<span id="cb25-6">    height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"600px"</span>,</span>
<span id="cb25-7">)</span></code></pre></div>
<p>Server:</p>
<div class="sourceCode" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb26-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.data_frame</span></span>
<span id="cb26-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> suggested_improvements():</span>
<span id="cb26-3">    res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb26-4"></span>
<span id="cb26-5">    req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb26-6"></span>
<span id="cb26-7">    evals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"evals"</span>].copy()</span>
<span id="cb26-8">    evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>]</span>
<span id="cb26-9"></span>
<span id="cb26-10">    result_table <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals.assign(</span>
<span id="cb26-11">        Category<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>],</span>
<span id="cb26-12">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Current score"</span>: evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>]},</span>
<span id="cb26-13">        Improvements<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"improvements"</span>],</span>
<span id="cb26-14">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score After Improvements"</span>: evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>]},</span>
<span id="cb26-15">        Gain<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>],</span>
<span id="cb26-16">    )[</span>
<span id="cb26-17">        [</span>
<span id="cb26-18">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Category"</span>,</span>
<span id="cb26-19">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Current score"</span>,</span>
<span id="cb26-20">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Improvements"</span>,</span>
<span id="cb26-21">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score After Improvements"</span>,</span>
<span id="cb26-22">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>,</span>
<span id="cb26-23">        ]</span>
<span id="cb26-24">    ].sort_values(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>, ascending<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb26-25"></span>
<span id="cb26-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> result_table</span></code></pre></div>
</div>
<div id="tabset-9-2" class="tab-pane" aria-labelledby="tabset-9-2-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
More fun table libraries
</div>
</div>
<div class="callout-body-container callout-body">
<p>Want to have some fun with JavaScript based tables? Check out <a href="https://glin.github.io/reactable/">reactable</a>, which is based on the <a href="https://github.com/tanstack/table/tree/v7">React Table</a> library. Or, go for <a href="https://gt.rstudio.com">gt</a>! You can get some inspo from the <a href="https://shiny.posit.co/r/components/">Shiny components gallery</a>.</p>
</div>
</div>
<p>UI:</p>
<div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb27-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb27-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">600</span>,</span>
<span id="cb27-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Suggested improvements per category"</span>)),</span>
<span id="cb27-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tableOutput</span>(</span>
<span id="cb27-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">outputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"suggested_improvements"</span></span>
<span id="cb27-6">  )</span>
<span id="cb27-7">)</span></code></pre></div>
<p>Server:</p>
<div class="sourceCode" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb28-1">output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>suggested_improvements <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderTable</span>({</span>
<span id="cb28-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb28-3"></span>
<span id="cb28-4">  evals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>evals</span>
<span id="cb28-5"></span>
<span id="cb28-6">  evals <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb28-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(score) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb28-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb28-9">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Gain =</span> score_after_improvements <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> score</span>
<span id="cb28-10">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb28-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(</span>
<span id="cb28-12">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Category =</span> category,</span>
<span id="cb28-13">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Current score</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> score,</span>
<span id="cb28-14">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Improvements =</span> improvements,</span>
<span id="cb28-15">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Score After Improvements</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> score_after_improvements,</span>
<span id="cb28-16">      Gain</span>
<span id="cb28-17">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb28-18">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">desc</span>(Gain))</span>
<span id="cb28-19">})</span></code></pre></div>
</div>
</div>
</div>
</section>
<section id="error-catching" class="level2">
<h2 class="anchored" data-anchor-id="error-catching">Error catching</h2>
<p>An error. The thing we don’t want to see in our app. Errors reduce user experience, big time. A frozen app or red error messages don’t make your users happy. And for you, as a developer, you’re not too happy about them either. Unfortunately, it’s hard to completely avoid getting errors: you’ll always see that there’s a specific scenario that you haven’t thought about. But that doesn’t mean you have to just let it happen: you can catch errors so your users are not confronted with a frozen app or messages they can’t understand. Instead, you can confront your users with a friendly (and hopefully useful) message.</p>
<p>One thing worth knowing: in Shiny, if an error happens inside a reactive expression or an observer, the default behaviour is for the app to crash. That’s because Shiny has no way of knowing whether the error is fatal or not, and it doesn’t really have a natural place to show that error to the user. With LLM apps, this becomes a bigger deal: errors happen often, they’re not always fatal, and it’s really frustrating for your users if the whole app crashes and they lose their conversation history. Models can return unexpected output, an API call might time out, the API might be overloaded by your requests (been there, done that), or the user could upload something your app doesn’t know how to handle. That’s why you want to deal with errors “gracefully”. And luckily, you don’t have to reinvent the wheel here. For example, the <code>Chat</code> component automatically catches errors that happen while streaming and shows the user a short explanation instead of breaking the whole app.</p>
<p>However, in a bespoke app like DeckCheck, where we’re streaming inside our own reactive expression or observer, you’ll want to think about setting up your own error handling. The idea is simple: don’t crash the app, and let the user know what went wrong in a friendly way. In our case, we split up our error messages: we can display one when something goes wrong with processing the Quarto file, and we can display one when our chat didn’t go as planned. These two error-catching “wrappers” serve as some inspiration for your next friendly error message.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-10-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-10-1" aria-controls="tabset-10-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-10-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-10-2" aria-controls="tabset-10-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-10-1" class="tab-pane active" aria-labelledby="tabset-10-1-tab">
<p>To demonstrate what an error-catching “wrapper” could look like, let’s take a look at error handling for the chat task:</p>
<div class="sourceCode" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb29-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.effect</span></span>
<span id="cb29-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> run_chat():</span>
<span id="cb29-3">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># require quarto_task result to be available</span></span>
<span id="cb29-4">    req(quarto_task.result() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb29-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">try</span>:</span>
<span id="cb29-6">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Error for testing</span></span>
<span id="cb29-7">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># raise ValueError("Test error")</span></span>
<span id="cb29-8"></span>
<span id="cb29-9">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb29-10"></span>
<span id="cb29-11">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trigger the chat task with the provided inputs</span></span>
<span id="cb29-12">        chat_task.invoke(system_prompt, markdown_content, DeckAnalysis)</span>
<span id="cb29-13"></span>
<span id="cb29-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb29-15">        warnings.warn(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Error when trying to invoke chat_task: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb29-16"></span>
<span id="cb29-17">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print stack trace to the console</span></span>
<span id="cb29-18">        traceback.print_exc()</span>
<span id="cb29-19"></span>
<span id="cb29-20">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return value that triggers modal in UI</span></span>
<span id="cb29-21">        m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.modal(</span>
<span id="cb29-22">            ui.div(</span>
<span id="cb29-23">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Sad bootstrap icon</span></span>
<span id="cb29-24">                ui.HTML(sad_icon),</span>
<span id="cb29-25">                ui.br(),</span>
<span id="cb29-26">                ui.p(</span>
<span id="cb29-27">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The not so Shiny Side of LLMs. Unfortunately, chatting didn't work out. Do you have enough credits left?"</span></span>
<span id="cb29-28">                ),</span>
<span id="cb29-29">                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># add class to center the content</span></span>
<span id="cb29-30">                class_<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center"</span>,</span>
<span id="cb29-31">            ),</span>
<span id="cb29-32">            title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Oops, something went wrong!"</span>,</span>
<span id="cb29-33">            easy_close<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb29-34">            footer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.modal_button(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>),</span>
<span id="cb29-35">        )</span>
<span id="cb29-36">        ui.modal_show(m)</span></code></pre></div>
<p>The same error handling gets applied to the Quarto task.</p>
<p>The Quarto task and the chat task chain together various tasks: copying an uploaded Quarto file, rendering it to Markdown and HTML, building a system prompt, and then invoking a conversation with an LLM. Any of these steps could fail (a bad upload, Quarto not rendering, the model returning something unexpected), but the <code>try/except</code> makes sure the app doesn’t just crash or leave the user hanging. Instead, if something goes wrong, it logs the error for debugging and then shows the user a clean modal with a simple message.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Error notifications
</div>
</div>
<div class="callout-body-container callout-body">
<p>There’s also a nice helper for error notifications: <a href="https://shiny.posit.co/py/docs/genai-stream.html#error-handling"><code>shiny.types.NotifyException</code></a>. This is what <code>ui.Chat</code> from <a href="https://posit-dev.github.io/shinychat/py/">shinychat</a> uses for its error notifications. You can use it like this:</p>
<div class="sourceCode" id="cb30" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb30-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb30-2">    msg <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"An error occurred: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb30-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> NotifyException(msg) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> e</span></code></pre></div>
</div>
</div>
</div>
<div id="tabset-10-2" class="tab-pane" aria-labelledby="tabset-10-2-tab">
<p>To demonstrate what an error-catching “wrapper” could look like, let’s take a look at error handling for the chat task:</p>
<div class="sourceCode" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb31-1">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb31-2">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">result</span>())</span>
<span id="cb31-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tryCatch</span>(</span>
<span id="cb31-4">      {</span>
<span id="cb31-5">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Error for testing</span></span>
<span id="cb31-6">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># stop("This is a test error.")</span></span>
<span id="cb31-7"></span>
<span id="cb31-8">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ...</span></span>
<span id="cb31-9"></span>
<span id="cb31-10">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trigger the chat task with the provided inputs</span></span>
<span id="cb31-11">        chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invoke</span>(</span>
<span id="cb31-12">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt,</span>
<span id="cb31-13">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">markdown_content =</span> markdown_content,</span>
<span id="cb31-14">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type_deck_analysis =</span> type_deck_analysis</span>
<span id="cb31-15">        )</span>
<span id="cb31-16">      },</span>
<span id="cb31-17">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(e) {</span>
<span id="cb31-18">        rlang<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">warn</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(</span>
<span id="cb31-19">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Error when trying to invoke chat_task:"</span>,</span>
<span id="cb31-20">          e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>message</span>
<span id="cb31-21">        ))</span>
<span id="cb31-22">        </span>
<span id="cb31-23">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print stack trace</span></span>
<span id="cb31-24">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(rlang<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trace_back</span>())</span>
<span id="cb31-25"></span>
<span id="cb31-26">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Show modal to the user</span></span>
<span id="cb31-27">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">showModal</span>(</span>
<span id="cb31-28">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">modalDialog</span>(</span>
<span id="cb31-29">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Oops! Something went wrong"</span>,</span>
<span id="cb31-30">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">div</span>(</span>
<span id="cb31-31">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center"</span>,</span>
<span id="cb31-32">              bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(</span>
<span id="cb31-33">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"emoji-frown-fill"</span>,</span>
<span id="cb31-34">                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2em"</span>,</span>
<span id="cb31-35">                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-warning"</span></span>
<span id="cb31-36">              ),</span>
<span id="cb31-37">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">br</span>(),</span>
<span id="cb31-38">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(</span>
<span id="cb31-39">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The not so Shiny Side of LLMs. Unfortunately, chatting didn't work out. Do you have enough credits left?"</span></span>
<span id="cb31-40">              )</span>
<span id="cb31-41">            ),</span>
<span id="cb31-42">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">easyClose =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb31-43">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">footer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">modalButton</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>)</span>
<span id="cb31-44">          )</span>
<span id="cb31-45">        )</span>
<span id="cb31-46">      }</span>
<span id="cb31-47">    )</span>
<span id="cb31-48">  })</span></code></pre></div>
<p>The same error handling gets applied to the Quarto task.</p>
<p>The Quarto task and the chat task chain together various tasks: copying an uploaded Quarto file, rendering it to Markdown and HTML, building a system prompt, and then invoking a conversation with an LLM. Any of these steps could fail (a bad upload, Quarto not rendering, the model returning something unexpected), but the <code>tryCatch</code> wrapper makes sure the app doesn’t just crash or leave the user hanging. Instead, if something goes wrong, it logs the error for debugging and then shows the user a clean modal with a simple message.</p>
</div>
</div>
</div>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-error-msg.png" class="img-fluid"></p>
<p>Note that the error messages just say “something went wrong” and a little direction as to what to do next. This is the cleanest and most “sanitised” way of handling errors. If you were to pass the real error message straight through to the modal, you’d risk showing users technical details they’re not supposed to see.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Oops, an error
</div>
</div>
<div class="callout-body-container callout-body">
<p>The <code>result()</code> method of <code>extended_task</code> (Python) / <code>ExtendedTask</code> (R) can return <code>"error"</code> too. If there’s an error in the task, the error will be re-thrown if you call the <code>result()</code> method.</p>
</div>
</div>
</section>
<section id="custom-csssass" class="level2">
<h2 class="anchored" data-anchor-id="custom-csssass">Custom CSS/Sass</h2>
<p>So far, we’ve been happily using a preset theme in our app (for DeckCheck: the Bootswatch Flatly theme). Nothing wrong with that, but maybe your organisation has a “house style” (company colours, logos, and fonts that need to be everywhere). The good news: Shiny doesn’t get in your way here. Under the hood, a Shiny app is still just HTML, CSS, and JavaScript. And just like any other web app, you can tweak the look and feel with CSS/<a href="https://rstudio.github.io/sass/">Sass</a> until it matches whatever look you’re going for.</p>
<p>In Shiny, adding that CSS/Sass is easy. There are actually a few options:</p>
<ul>
<li><strong>Inline CSS (global)</strong>: you can attach CSS directly using a <code>&lt;style&gt;</code> tag (<code>tags.style()</code> in Python or <code>tags$style()</code> in R) at the top of your UI. This is handy when you just need a couple of tweaks that apply globally. You can use it for CSS classes that you use throughout your app, or for things like global fonts. For simplicity, we’re going for this option in DeckCheck. Note that if you want to add add styles in the <code>&lt;head&gt;</code> specifically, you need <code>ui.head_content()</code> (Python) / <code>tags$head()</code> (R).</li>
<li><strong>Inline CSS (individual components)</strong>: when you only need to tweak one particular element (e.g.&nbsp;center a particular element), you can append styles directly to that tag. In Python, you can call <a href="https://shiny.posit.co/py/api/core/TagTypes.html#htmltools.Tag.add_style"><code>.add_style()</code></a> on the tag itself. In R, you’d use <a href="https://shiny.posit.co/r/reference/shiny/latest/tagappendattributes.html"><code>tagAppendAttributes()</code></a>. Note that all HTML tags and a lot of UI components also support <code>style</code> and <code>class</code> arguments, so make sure to check out the documentation if you’re looking to make adjustments.</li>
<li><strong>External stylesheets</strong>: if your styles grow beyond a few lines, you’ll want to put it in a (S)CSS file inside the <code>www/</code> folder. Shiny will automatically serve that file, and you can include it with <code>tags.link()</code> / <code>include_css()</code> in Python or <code>tags$link()</code> / <code>includeCSS()</code> in R. This keeps your UI code clean and makes your styles easier to manage.</li>
</ul>
<p>In DeckCheck, we add the styles of the <code>.bounce</code> class that we use for our bouncing robot.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-11-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-11-1" aria-controls="tabset-11-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-11-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-11-2" aria-controls="tabset-11-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-11-1" class="tab-pane active" aria-labelledby="tabset-11-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Custom CSS in Shiny
</div>
</div>
<div class="callout-body-container callout-body">
<p>If you want to learn more about styling your app, check out <a href="https://shiny.posit.co/py/docs/ui-customize.html#custom-css">Custom CSS in Shiny for Python</a>.</p>
</div>
</div>
<div class="sourceCode" id="cb32" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb32-1">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fillable(</span>
<span id="cb32-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## General theme and styles</span></span>
<span id="cb32-3">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## 1. Custom CSS</span></span>
<span id="cb32-4">    ui.tags.style(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb32-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        .bounce {</span></span>
<span id="cb32-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            animation: bounce 2s infinite;</span></span>
<span id="cb32-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        }</span></span>
<span id="cb32-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        @keyframes bounce {</span></span>
<span id="cb32-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            0%, 100% {</span></span>
<span id="cb32-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">                transform: translateY(0);</span></span>
<span id="cb32-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            }</span></span>
<span id="cb32-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            50% {</span></span>
<span id="cb32-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">                transform: translateY(-20px);</span></span>
<span id="cb32-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            }</span></span>
<span id="cb32-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        }</span></span>
<span id="cb32-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    """</span>),</span>
<span id="cb32-17">    ui.layout_sidebar(</span>
<span id="cb32-18">        ui.sidebar(</span>
<span id="cb32-19">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## Sidebar content</span></span>
<span id="cb32-20">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## ...</span></span>
<span id="cb32-21">        ),</span>
<span id="cb32-22">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## Main content</span></span>
<span id="cb32-23">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## ...</span></span>
<span id="cb32-24">    ),</span>
<span id="cb32-25">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Bootswatch theme</span></span>
<span id="cb32-26">    theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shinyswatch.theme.flatly,</span>
<span id="cb32-27">)</span></code></pre></div>
<p>Looking for more “high-level” styling? Check out <a href="https://posit-dev.github.io/brand-yml/">brand.yml</a> for branding with a simple YAML file.</p>
</div>
<div id="tabset-11-2" class="tab-pane" aria-labelledby="tabset-11-2-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Custom CSS in Shiny
</div>
</div>
<div class="callout-body-container callout-body">
<p>If you want to learn more about styling your app, check out <a href="https://shiny.posit.co/r/articles/build/css">Custom CSS in Shiny for R</a>.</p>
</div>
</div>
<div class="sourceCode" id="cb33" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb33-1">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fillable</span>(</span>
<span id="cb33-2">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Options</span></span>
<span id="cb33-3">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## 1. Bootswatch theme</span></span>
<span id="cb33-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bootswatch =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flatly"</span>),</span>
<span id="cb33-5">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## 2. Custom CSS</span></span>
<span id="cb33-6">  tags<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">style</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">HTML</span>(</span>
<span id="cb33-7">    .bounce {</span>
<span id="cb33-8">      animation<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> bounce <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>s infinite;</span>
<span id="cb33-9">    }</span>
<span id="cb33-10">    <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span>keyframes bounce {</span>
<span id="cb33-11">      <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%, 100%</span> {</span>
<span id="cb33-12">        transform<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">translateY</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>);</span>
<span id="cb33-13">      }</span>
<span id="cb33-14">      <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>% {</span>
<span id="cb33-15">        transform<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">translateY</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>px);</span>
<span id="cb33-16">      }</span>
<span id="cb33-17">    }</span>
<span id="cb33-18">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb33-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  )),</span></span>
<span id="cb33-20"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  ## Layout</span></span>
<span id="cb33-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  layout_sidebar(</span></span>
<span id="cb33-22"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    ## Sidebar content</span></span>
<span id="cb33-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    sidebar = sidebar(</span></span>
<span id="cb33-24"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    ## Sidebar content</span></span>
<span id="cb33-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    ## ...</span></span>
<span id="cb33-26"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    ),</span></span>
<span id="cb33-27"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    ## Main content</span></span>
<span id="cb33-28"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      ## ...</span></span>
<span id="cb33-29"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  )</span></span>
<span id="cb33-30"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div>
<p>Note that these are fairly “low-level” techniques for achieving custom styling. To learn more about higher-level options you can take a look at <a href="https://rstudio.github.io/bslib/articles/theming/index.html">theming</a> or <a href="https://rstudio.github.io/bslib/articles/brand-yml/index.html">brand.yml</a>.</p>
</div>
</div>
</div>
</section>
</section>
<section id="the-end-result" class="level1">
<h1>The end result</h1>
<p>If we combine everything we talked about, we end up with a polished DeckCheck app! A sidebar layout with users inputs, a nice loading experience, non-blocking async operations, fancy looking graphs and tables, tooltips, you name it.</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-deckcheck-full.gif" class="img-fluid"></p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-12-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-12-1" aria-controls="tabset-12-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-12-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-12-2" aria-controls="tabset-12-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-12-1" class="tab-pane active" aria-labelledby="tabset-12-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Get this code from GitHub
</div>
</div>
<div class="callout-body-container callout-body">
<p>You can grab the code directly from <a href="https://github.com/hypebright/the-shiny-side-of-llms/blob/d1094d2774f9d0c213c7ddf6e17f94da706b1b76/Py/deckcheck/app.py">here</a>.</p>
</div>
</div>
<div class="cell">
<details class="code-fold">
<summary>See full app</summary>
<div class="sourceCode cell-code" id="cb34" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb34-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> App, reactive, render, ui, req</span>
<span id="cb34-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shinywidgets <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> output_widget, render_widget</span>
<span id="cb34-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> shinyswatch</span>
<span id="cb34-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> pandas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> pd</span>
<span id="cb34-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> plotly.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> px</span>
<span id="cb34-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb34-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic, interpolate_file, interpolate</span>
<span id="cb34-8"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb34-9"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pydantic <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> BaseModel, Field</span>
<span id="cb34-10"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> typing <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Annotated, Optional, Union</span>
<span id="cb34-11"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> tempfile</span>
<span id="cb34-12"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> shutil</span>
<span id="cb34-13"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> asyncio</span>
<span id="cb34-14"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> warnings</span>
<span id="cb34-15"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> traceback</span>
<span id="cb34-16"></span>
<span id="cb34-17">load_dotenv()  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Loads key from the .env file</span></span>
<span id="cb34-18"></span>
<span id="cb34-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Path to the current file</span></span>
<span id="cb34-20">APP_DIR <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>).parent</span>
<span id="cb34-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Root directory of the project</span></span>
<span id="cb34-22">ROOT_DIR <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> APP_DIR.parent.parent</span>
<span id="cb34-23"></span>
<span id="cb34-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Data Structure</span></span>
<span id="cb34-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-27">ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)]</span>
<span id="cb34-28">PercentType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100.0</span>)]</span>
<span id="cb34-29">MinutesType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb34-30">SlideCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb34-31"></span>
<span id="cb34-32"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> ScoringCategory(BaseModel):</span>
<span id="cb34-33">    score: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1–10."</span>)</span>
<span id="cb34-34">    justification: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span>)</span>
<span id="cb34-35">    improvements: Optional[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-36">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>,</span>
<span id="cb34-37">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb34-38">    )</span>
<span id="cb34-39">    score_after_improvements: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-40">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb34-41">    )</span>
<span id="cb34-42"></span>
<span id="cb34-43"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> DeckAnalysis(BaseModel):</span>
<span id="cb34-44">    presentation_title: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>)</span>
<span id="cb34-45">    total_slides: SlideCount</span>
<span id="cb34-46">    percent_with_code: PercentType</span>
<span id="cb34-47">    percent_with_images: PercentType</span>
<span id="cb34-48">    estimated_duration_minutes: MinutesType</span>
<span id="cb34-49">    tone: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-50">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the tone of the presentation."</span></span>
<span id="cb34-51">    )</span>
<span id="cb34-52"></span>
<span id="cb34-53">    clarity: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-54">        ...,</span>
<span id="cb34-55">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb34-56">    )</span>
<span id="cb34-57">    relevance: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-58">        ...,</span>
<span id="cb34-59">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Assess how well the content matches the audience's background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb34-60">    )</span>
<span id="cb34-61">    visual_design: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-62">        ...,</span>
<span id="cb34-63">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb34-64">    )</span>
<span id="cb34-65">    engagement: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-66">        ...,</span>
<span id="cb34-67">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb34-68">    )</span>
<span id="cb34-69">    pacing: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-70">        ...,</span>
<span id="cb34-71">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb34-72">    )</span>
<span id="cb34-73">    structure: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-74">        ...,</span>
<span id="cb34-75">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb34-76">    )</span>
<span id="cb34-77">    consistency: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-78">        ...,</span>
<span id="cb34-79">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb34-80">    )</span>
<span id="cb34-81">    accessibility: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb34-82">        ...,</span>
<span id="cb34-83">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb34-84">    )</span>
<span id="cb34-85"></span>
<span id="cb34-86"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-87"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Tool definition</span></span>
<span id="cb34-88"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-89"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> calculate_slide_metric(metric: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> Union[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>]:</span>
<span id="cb34-90">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb34-91"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb34-92"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb34-93"></span>
<span id="cb34-94"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Parameters</span></span>
<span id="cb34-95"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    ----------</span></span>
<span id="cb34-96"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    metric : str</span></span>
<span id="cb34-97"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb34-98"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        "code" for percentage of slides containing fenced code blocks,</span></span>
<span id="cb34-99"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        or "images" for percentage of slides containing images.</span></span>
<span id="cb34-100"></span>
<span id="cb34-101"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Returns</span></span>
<span id="cb34-102"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    -------</span></span>
<span id="cb34-103"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    float or int</span></span>
<span id="cb34-104"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The calculated metric value.</span></span>
<span id="cb34-105"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb34-106">    html_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.html"</span>)</span>
<span id="cb34-107">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> html_file.exists():</span>
<span id="cb34-108">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">FileNotFoundError</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"HTML file </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>html_file<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> does not exist."</span>)</span>
<span id="cb34-109"></span>
<span id="cb34-110">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb34-111">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(html_file, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"r"</span>, encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> f:</span>
<span id="cb34-112">        html_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> f.read()</span>
<span id="cb34-113"></span>
<span id="cb34-114">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb34-115">    slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> html_content.split(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>)</span>
<span id="cb34-116">    total_slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(slides)</span>
<span id="cb34-117"></span>
<span id="cb34-118">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>:</span>
<span id="cb34-119">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> total_slides</span>
<span id="cb34-120">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>:</span>
<span id="cb34-121">        slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb34-122">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb34-123">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>:</span>
<span id="cb34-124">        slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;img"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb34-125">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb34-126">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb34-127">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">ValueError</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb34-128"></span>
<span id="cb34-129">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> result</span>
<span id="cb34-130"></span>
<span id="cb34-131"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-132"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Data wrangling</span></span>
<span id="cb34-133"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-134"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> make_frames(d: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">dict</span>):</span>
<span id="cb34-135">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb34-136"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Convert the dictionary returned by the LLM into a meta dictionary and</span></span>
<span id="cb34-137"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    a DataFrame for the eval categories.</span></span>
<span id="cb34-138"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Parameters</span></span>
<span id="cb34-139"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    ----------</span></span>
<span id="cb34-140"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    d : dict</span></span>
<span id="cb34-141"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The dictionary returned by the LLM.</span></span>
<span id="cb34-142"></span>
<span id="cb34-143"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Returns</span></span>
<span id="cb34-144"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    -------</span></span>
<span id="cb34-145"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    dict</span></span>
<span id="cb34-146"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        A dictionary with two keys: "meta" and "evals". "meta" contains the</span></span>
<span id="cb34-147"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        meta information as a dictionary, and "evals" contains a DataFrame with</span></span>
<span id="cb34-148"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        the eval categories.</span></span>
<span id="cb34-149"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb34-150">    meta_keys <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [</span>
<span id="cb34-151">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"presentation_title"</span>,</span>
<span id="cb34-152">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>,</span>
<span id="cb34-153">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"percent_with_code"</span>,</span>
<span id="cb34-154">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"percent_with_images"</span>,</span>
<span id="cb34-155">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"estimated_duration_minutes"</span>,</span>
<span id="cb34-156">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tone"</span>,</span>
<span id="cb34-157">    ]</span>
<span id="cb34-158">    meta <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {k: d[k] <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> k <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> meta_keys}</span>
<span id="cb34-159"></span>
<span id="cb34-160">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># eval categories (everything else)</span></span>
<span id="cb34-161">    evals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb34-162">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> k, v <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> d.items():</span>
<span id="cb34-163">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> k <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> meta_keys:</span>
<span id="cb34-164">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># fix typo</span></span>
<span id="cb34-165">            evals.append(</span>
<span id="cb34-166">                {</span>
<span id="cb34-167">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>: k,</span>
<span id="cb34-168">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>: v[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>],</span>
<span id="cb34-169">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification"</span>: v[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification"</span>],</span>
<span id="cb34-170">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"improvements"</span>: v[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"improvements"</span>],</span>
<span id="cb34-171">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>: v[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>],</span>
<span id="cb34-172">                }</span>
<span id="cb34-173">            )</span>
<span id="cb34-174"></span>
<span id="cb34-175">    evals_df <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.DataFrame(evals)</span>
<span id="cb34-176"></span>
<span id="cb34-177">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"meta"</span>: meta, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"evals"</span>: evals_df}</span>
<span id="cb34-178"></span>
<span id="cb34-179"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-180"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Tooltip helper</span></span>
<span id="cb34-181"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-182"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> add_line_breaks(text, width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>):</span>
<span id="cb34-183">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">isinstance</span>(text, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb34-184">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> text</span>
<span id="cb34-185"></span>
<span id="cb34-186">    words <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> text.split()</span>
<span id="cb34-187">    lines <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb34-188">    current_line <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span></span>
<span id="cb34-189"></span>
<span id="cb34-190">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> word <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> words:</span>
<span id="cb34-191">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># +1 accounts for the space if current_line isn't empty</span></span>
<span id="cb34-192">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(current_line) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(word) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> current_line <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> width:</span>
<span id="cb34-193">            current_line <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" "</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> current_line <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> word</span>
<span id="cb34-194">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb34-195">            lines.append(current_line)</span>
<span id="cb34-196">            current_line <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> word</span>
<span id="cb34-197"></span>
<span id="cb34-198">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> current_line:</span>
<span id="cb34-199">        lines.append(current_line)</span>
<span id="cb34-200"></span>
<span id="cb34-201">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;br&gt;"</span>.join(lines)</span>
<span id="cb34-202"></span>
<span id="cb34-203"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-204"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Icons</span></span>
<span id="cb34-205"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-206"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># SVG icon copied from https://icons.getbootstrap.com/icons/file-slides-fill/</span></span>
<span id="cb34-207">file_slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-slides-fill" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb34-208"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M7 7.78V5.22c0-.096.106-.156.19-.106l2.13 1.279a.125.125 0 0 1 0 .214l-2.13 1.28A.125.125 0 0 1 7 7.778z"/&gt;</span></span>
<span id="cb34-209"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M12 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M5 4h6a.5.5 0 0 1 .496.438l.5 4A.5.5 0 0 1 11.5 9h-3v2.016c.863.055 1.5.251 1.5.484 0 .276-.895.5-2 .5s-2-.224-2-.5c0-.233.637-.429 1.5-.484V9h-3a.5.5 0 0 1-.496-.562l.5-4A.5.5 0 0 1 5 4"/&gt;</span></span>
<span id="cb34-210"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb34-211"></span>
<span id="cb34-212">file_slides_loader <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" width="6em" height="6em" fill="currentColor" class="bi bi-file-slides bounce" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb34-213"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M5 4a.5.5 0 0 0-.496.438l-.5 4A.5.5 0 0 0 4.5 9h3v2.016c-.863.055-1.5.251-1.5.484 0 .276.895.5 2 .5s2-.224 2-.5c0-.233-.637-.429-1.5-.484V9h3a.5.5 0 0 0 .496-.562l-.5-4A.5.5 0 0 0 11 4zm2 3.78V5.22c0-.096.106-.156.19-.106l2.13 1.279a.125.125 0 0 1 0 .214l-2.13 1.28A.125.125 0 0 1 7 7.778z"/&gt;</span></span>
<span id="cb34-214"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/&gt;</span></span>
<span id="cb34-215"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb34-216"></span>
<span id="cb34-217"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># SVG icon copied from https://icons.getbootstrap.com/icons/file-code-fill/</span></span>
<span id="cb34-218">file_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-code-fill" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb34-219"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M12 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M6.646 5.646a.5.5 0 1 1 .708.708L5.707 8l1.647 1.646a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708zm2.708 0 2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L10.293 8 8.646 6.354a.5.5 0 1 1 .708-.708"/&gt;</span></span>
<span id="cb34-220"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb34-221"></span>
<span id="cb34-222"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># SVG icon copied from https://icons.getbootstrap.com/icons/file-image-fill/</span></span>
<span id="cb34-223">file_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-file-image-fill" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb34-224"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M4 0h8a2 2 0 0 1 2 2v8.293l-2.73-2.73a1 1 0 0 0-1.52.127l-1.889 2.644-1.769-1.062a1 1 0 0 0-1.222.15L2 12.292V2a2 2 0 0 1 2-2m4.002 5.5a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0"/&gt;</span></span>
<span id="cb34-225"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M10.564 8.27 14 11.708V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-.293l3.578-3.577 2.56 1.536 2.426-3.395z"/&gt;</span></span>
<span id="cb34-226"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb34-227"></span>
<span id="cb34-228"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># SVG icon copied from https://icons.getbootstrap.com/icons/robot/</span></span>
<span id="cb34-229">robot <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-robot" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb34-230"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135"/&gt;</span></span>
<span id="cb34-231"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5"/&gt;</span></span>
<span id="cb34-232"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb34-233"></span>
<span id="cb34-234">robot_loader <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" width="6em" height="6em" fill="currentColor" class="bi bi-robot text-primary bounce" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb34-235"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135"/&gt;</span></span>
<span id="cb34-236"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5"/&gt;</span></span>
<span id="cb34-237"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb34-238"></span>
<span id="cb34-239"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># SVG icon copied from https://icons.getbootstrap.com/icons/emoji-frown-fill/</span></span>
<span id="cb34-240">sad_icon <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""&lt;svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-emoji-frown-fill text-warning" viewBox="0 0 16 16"&gt;</span></span>
<span id="cb34-241"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  &lt;path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16M7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5m-2.715 5.933a.5.5 0 0 1-.183-.683A4.5 4.5 0 0 1 8 9.5a4.5 4.5 0 0 1 3.898 2.25.5.5 0 0 1-.866.5A3.5 3.5 0 0 0 8 10.5a3.5 3.5 0 0 0-3.032 1.75.5.5 0 0 1-.683.183M10 8c-.552 0-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5S10.552 8 10 8"/&gt;</span></span>
<span id="cb34-242"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;/svg&gt;"""</span></span>
<span id="cb34-243"></span>
<span id="cb34-244"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-245"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny App</span></span>
<span id="cb34-246"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb34-247">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fillable(</span>
<span id="cb34-248">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## General theme and styles</span></span>
<span id="cb34-249">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## 1. Custom CSS</span></span>
<span id="cb34-250">    ui.tags.style(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb34-251"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        .bounce {</span></span>
<span id="cb34-252"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            animation: bounce 2s infinite;</span></span>
<span id="cb34-253"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        }</span></span>
<span id="cb34-254"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        @keyframes bounce {</span></span>
<span id="cb34-255"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            0%, 100% {</span></span>
<span id="cb34-256"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">                transform: translateY(0);</span></span>
<span id="cb34-257"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            }</span></span>
<span id="cb34-258"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            50% {</span></span>
<span id="cb34-259"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">                transform: translateY(-20px);</span></span>
<span id="cb34-260"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">            }</span></span>
<span id="cb34-261"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        }</span></span>
<span id="cb34-262"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    """</span>),</span>
<span id="cb34-263">    ui.layout_sidebar(</span>
<span id="cb34-264">        ui.sidebar(</span>
<span id="cb34-265">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## Sidebar content</span></span>
<span id="cb34-266">            ui.div(</span>
<span id="cb34-267">                ui.p(ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hey, I am DeckCheck!"</span>)),</span>
<span id="cb34-268">                ui.p(</span>
<span id="cb34-269">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""I can help you improve your Quarto presentations by analysing them and suggesting improvements. Before I can do that, I need some information about your presentation."""</span></span>
<span id="cb34-270">                ),</span>
<span id="cb34-271">            ),</span>
<span id="cb34-272">            ui.input_file(</span>
<span id="cb34-273">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span>,</span>
<span id="cb34-274">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Upload your Quarto presentation"</span>,</span>
<span id="cb34-275">                accept<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmdx"</span>],</span>
<span id="cb34-276">                multiple<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb34-277">            ),</span>
<span id="cb34-278">            ui.input_text_area(</span>
<span id="cb34-279">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>,</span>
<span id="cb34-280">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Describe your audience"</span>,</span>
<span id="cb34-281">                height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"150px"</span>,</span>
<span id="cb34-282">                placeholder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span>,</span>
<span id="cb34-283">            ),</span>
<span id="cb34-284">            ui.input_numeric(</span>
<span id="cb34-285">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time cap for the presentation (minutes)"</span>, value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb34-286">            ),</span>
<span id="cb34-287">            ui.input_text(</span>
<span id="cb34-288">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>,</span>
<span id="cb34-289">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Type of talk"</span>,</span>
<span id="cb34-290">                placeholder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. lightning talk, workshop, or keynote"</span>,</span>
<span id="cb34-291">            ),</span>
<span id="cb34-292">            ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Event name"</span>, placeholder<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. posit::conf(2025)"</span>),</span>
<span id="cb34-293">            ui.input_task_button(</span>
<span id="cb34-294">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>,</span>
<span id="cb34-295">                icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(robot),</span>
<span id="cb34-296">                label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyse presentation"</span>,</span>
<span id="cb34-297">            ),</span>
<span id="cb34-298">            width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>,</span>
<span id="cb34-299">        ),</span>
<span id="cb34-300">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">## Main content</span></span>
<span id="cb34-301">        ui.output_ui(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results"</span>),</span>
<span id="cb34-302">    ),</span>
<span id="cb34-303">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Bootswatch theme</span></span>
<span id="cb34-304">    theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>shinyswatch.theme.flatly,</span>
<span id="cb34-305">)</span>
<span id="cb34-306"></span>
<span id="cb34-307"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, output, session):</span>
<span id="cb34-308">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@ui.bind_task_button</span>(button_id<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb34-309">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.extended_task</span></span>
<span id="cb34-310">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> quarto_task(file_path, temp_dir):</span>
<span id="cb34-311">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an Extended Task to avoid blocking. Note that</span></span>
<span id="cb34-312">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a temporary directory called within mirai will be</span></span>
<span id="cb34-313">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># different from the one in the "main" Shiny session. Hence,</span></span>
<span id="cb34-314">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we pass a temp_dir parameter to the task and use that.</span></span>
<span id="cb34-315">        qmd_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(temp_dir) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.qmd"</span></span>
<span id="cb34-316">        shutil.copy(file_path, qmd_file)</span>
<span id="cb34-317"></span>
<span id="cb34-318">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Run asyncio subprocess</span></span>
<span id="cb34-319">        proc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> asyncio.create_subprocess_exec(</span>
<span id="cb34-320">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(qmd_file), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown,html"</span></span>
<span id="cb34-321">        )</span>
<span id="cb34-322">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> proc.communicate()</span>
<span id="cb34-323"></span>
<span id="cb34-324">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return the path to the markdown file</span></span>
<span id="cb34-325">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> Path(temp_dir) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.md"</span></span>
<span id="cb34-326"></span>
<span id="cb34-327">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@ui.bind_task_button</span>(button_id<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb34-328">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.extended_task</span></span>
<span id="cb34-329">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> chat_task(system_prompt, markdown_content, DeckAnalysis):</span>
<span id="cb34-330">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an extended task to avoid blocking the session and</span></span>
<span id="cb34-331">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we start a fresh chat session each time.</span></span>
<span id="cb34-332">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># For a feedback loop, we would use a persistent chat session.</span></span>
<span id="cb34-333">        chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb34-334">            model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb34-335">            system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb34-336">        )</span>
<span id="cb34-337"></span>
<span id="cb34-338">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set model parameters (optional)</span></span>
<span id="cb34-339">        chat.set_model_params(</span>
<span id="cb34-340">            temperature<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb34-341">        )</span>
<span id="cb34-342"></span>
<span id="cb34-343">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb34-344">        chat.register_tool(calculate_slide_metric)</span>
<span id="cb34-345"></span>
<span id="cb34-346">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb34-347">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb34-348">        chat_res1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.chat_async(</span>
<span id="cb34-349">            interpolate(</span>
<span id="cb34-350">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb34-351">            )</span>
<span id="cb34-352">        )</span>
<span id="cb34-353"></span>
<span id="cb34-354">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(chat_res1)</span>
<span id="cb34-355"></span>
<span id="cb34-356">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb34-357">        chat_res2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> chat.chat_structured_async(</span>
<span id="cb34-358">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb34-359">            data_model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>DeckAnalysis,</span>
<span id="cb34-360">        )</span>
<span id="cb34-361"></span>
<span id="cb34-362">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> chat_res2</span>
<span id="cb34-363"></span>
<span id="cb34-364">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.effect</span></span>
<span id="cb34-365">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.event</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.submit)</span>
<span id="cb34-366">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> run_quarto():</span>
<span id="cb34-367">        req(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb34-368">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">try</span>:</span>
<span id="cb34-369">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Error for testing</span></span>
<span id="cb34-370">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># raise ValueError("Test error")</span></span>
<span id="cb34-371"></span>
<span id="cb34-372">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get file path of the uploaded file</span></span>
<span id="cb34-373">            file_path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">file</span>()[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"datapath"</span>]</span>
<span id="cb34-374"></span>
<span id="cb34-375">            quarto_task.invoke(file_path, tempfile.gettempdir())</span>
<span id="cb34-376"></span>
<span id="cb34-377">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb34-378">            warnings.warn(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Error when trying to invoke quarto_task: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb34-379"></span>
<span id="cb34-380">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print stack trace to the console</span></span>
<span id="cb34-381">            traceback.print_exc()</span>
<span id="cb34-382"></span>
<span id="cb34-383">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return value that triggers modal in UI</span></span>
<span id="cb34-384">            m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.modal(</span>
<span id="cb34-385">                ui.div(</span>
<span id="cb34-386">                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Sad bootstrap icon</span></span>
<span id="cb34-387">                    ui.HTML(sad_icon),</span>
<span id="cb34-388">                    ui.br(),</span>
<span id="cb34-389">                    ui.p(</span>
<span id="cb34-390">                        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The not so Shiny Side of LLMs. Please check that your Quarto presentation is valid and contains slides."</span></span>
<span id="cb34-391">                    ),</span>
<span id="cb34-392">                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># add class to center the content</span></span>
<span id="cb34-393">                    class_<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center"</span>,</span>
<span id="cb34-394">                ),</span>
<span id="cb34-395">                title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Oops, something went wrong!"</span>,</span>
<span id="cb34-396">                easy_close<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb34-397">                footer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.modal_button(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>),</span>
<span id="cb34-398">            )</span>
<span id="cb34-399">            ui.modal_show(m)</span>
<span id="cb34-400"></span>
<span id="cb34-401">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.effect</span></span>
<span id="cb34-402">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> run_chat():</span>
<span id="cb34-403">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># require quarto_task result to be available</span></span>
<span id="cb34-404">        req(quarto_task.result() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb34-405">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">try</span>:</span>
<span id="cb34-406">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Error for testing</span></span>
<span id="cb34-407">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># raise ValueError("Test error")</span></span>
<span id="cb34-408"></span>
<span id="cb34-409">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get the Markdown file path from the complete quarto_task</span></span>
<span id="cb34-410">            markdown_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> quarto_task.result()</span>
<span id="cb34-411">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing the slides</span></span>
<span id="cb34-412">            markdown_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> markdown_file.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb34-413"></span>
<span id="cb34-414">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb34-415">            system_prompt_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb34-416">                ROOT_DIR <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompts"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompt-analyse-slides-structured-tool.md"</span></span>
<span id="cb34-417">            )</span>
<span id="cb34-418"></span>
<span id="cb34-419">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb34-420">            system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb34-421">                system_prompt_file,</span>
<span id="cb34-422">                variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb34-423">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.audience(),</span>
<span id="cb34-424">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.length(),</span>
<span id="cb34-425">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">type</span>(),</span>
<span id="cb34-426">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.event(),</span>
<span id="cb34-427">                    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown_content"</span>: markdown_content,</span>
<span id="cb34-428">                },</span>
<span id="cb34-429">            )</span>
<span id="cb34-430"></span>
<span id="cb34-431">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trigger the chat task with the provided inputs</span></span>
<span id="cb34-432">            chat_task.invoke(system_prompt, markdown_content, DeckAnalysis)</span>
<span id="cb34-433"></span>
<span id="cb34-434">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">Exception</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> e:</span>
<span id="cb34-435">            warnings.warn(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Error when trying to invoke chat_task: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb34-436"></span>
<span id="cb34-437">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print stack trace to the console</span></span>
<span id="cb34-438">            traceback.print_exc()</span>
<span id="cb34-439"></span>
<span id="cb34-440">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return value that triggers modal in UI</span></span>
<span id="cb34-441">            m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.modal(</span>
<span id="cb34-442">                ui.div(</span>
<span id="cb34-443">                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Sad bootstrap icon</span></span>
<span id="cb34-444">                    ui.HTML(sad_icon),</span>
<span id="cb34-445">                    ui.br(),</span>
<span id="cb34-446">                    ui.p(</span>
<span id="cb34-447">                        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The not so Shiny Side of LLMs. Unfortunately, chatting didn't work out. Do you have enough credits left?"</span></span>
<span id="cb34-448">                    ),</span>
<span id="cb34-449">                    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># add class to center the content</span></span>
<span id="cb34-450">                    class_<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center"</span>,</span>
<span id="cb34-451">                ),</span>
<span id="cb34-452">                title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Oops, something went wrong!"</span>,</span>
<span id="cb34-453">                easy_close<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb34-454">                footer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.modal_button(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>),</span>
<span id="cb34-455">            )</span>
<span id="cb34-456">            ui.modal_show(m)</span>
<span id="cb34-457"></span>
<span id="cb34-458">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.calc</span></span>
<span id="cb34-459">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> analysis_result():</span>
<span id="cb34-460">        res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> chat_task.result()</span>
<span id="cb34-461">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>:</span>
<span id="cb34-462">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> make_frames(res)</span>
<span id="cb34-463">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb34-464">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb34-465"></span>
<span id="cb34-466">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.ui</span></span>
<span id="cb34-467">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> results():</span>
<span id="cb34-468">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> quarto_task.status() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>:</span>
<span id="cb34-469">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.div(</span>
<span id="cb34-470">                ui.HTML(file_slides_loader),</span>
<span id="cb34-471">                ui.br(),</span>
<span id="cb34-472">                ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Processing your Quarto presentation..."</span>),</span>
<span id="cb34-473">                class_<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb34-474">                style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%"</span>,</span>
<span id="cb34-475">            )</span>
<span id="cb34-476"></span>
<span id="cb34-477">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> chat_task.status() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>:</span>
<span id="cb34-478">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.div(</span>
<span id="cb34-479">                ui.HTML(robot_loader),</span>
<span id="cb34-480">                ui.br(),</span>
<span id="cb34-481">                ui.p(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The LLM is doing its magic..."</span>),</span>
<span id="cb34-482">                class_<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb34-483">                style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%"</span>,</span>
<span id="cb34-484">            )</span>
<span id="cb34-485"></span>
<span id="cb34-486">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> chat_task.status() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"success"</span>:</span>
<span id="cb34-487">            <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.TagList(</span>
<span id="cb34-488">                ui.layout_column_wrap(</span>
<span id="cb34-489">                    ui.tooltip(</span>
<span id="cb34-490">                        ui.value_box(</span>
<span id="cb34-491">                            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Showtime"</span>,</span>
<span id="cb34-492">                            ui.output_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"showtime"</span>),</span>
<span id="cb34-493">                            showcase<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(file_slides),</span>
<span id="cb34-494">                            theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span>,</span>
<span id="cb34-495">                        ),</span>
<span id="cb34-496">                        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Slides are being counted based on the provided Quarto presentation, then an educated guess is made about the time it will take to present them."</span>,</span>
<span id="cb34-497">                    ),</span>
<span id="cb34-498">                    ui.tooltip(</span>
<span id="cb34-499">                        ui.value_box(</span>
<span id="cb34-500">                            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Code Savviness"</span>,</span>
<span id="cb34-501">                            ui.output_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code_savviness"</span>),</span>
<span id="cb34-502">                            showcase<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(file_code),</span>
<span id="cb34-503">                            theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span>,</span>
<span id="cb34-504">                        ),</span>
<span id="cb34-505">                        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Code Saviness is calculated based on the slides that contain code chunks. The percentage is the ratio of those slides to total slides."</span>,</span>
<span id="cb34-506">                    ),</span>
<span id="cb34-507">                    ui.tooltip(</span>
<span id="cb34-508">                        ui.value_box(</span>
<span id="cb34-509">                            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Image Presence"</span>,</span>
<span id="cb34-510">                            ui.output_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"image_presence"</span>),</span>
<span id="cb34-511">                            showcase<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.HTML(file_image),</span>
<span id="cb34-512">                            theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span>,</span>
<span id="cb34-513">                        ),</span>
<span id="cb34-514">                        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Image Presence is calculated based on the slides that contain images. The percentage is the ratio of those slides to total slides."</span>,</span>
<span id="cb34-515">                    ),</span>
<span id="cb34-516">                    width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>,</span>
<span id="cb34-517">                    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb34-518">                ),</span>
<span id="cb34-519">                ui.layout_column_wrap(</span>
<span id="cb34-520">                    ui.card(</span>
<span id="cb34-521">                        ui.card_header(ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Scores per category"</span>)),</span>
<span id="cb34-522">                        output_widget(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scores"</span>),</span>
<span id="cb34-523">                        height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"600px"</span>,</span>
<span id="cb34-524">                    ),</span>
<span id="cb34-525">                    ui.card(</span>
<span id="cb34-526">                        ui.card_header(</span>
<span id="cb34-527">                            ui.strong(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Suggested improvements per category"</span>)</span>
<span id="cb34-528">                        ),</span>
<span id="cb34-529">                        ui.output_data_frame(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"suggested_improvements"</span>),</span>
<span id="cb34-530">                        height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"600px"</span>,</span>
<span id="cb34-531">                    ),</span>
<span id="cb34-532">                    width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb34-533">                    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb34-534">                ),</span>
<span id="cb34-535">            )</span>
<span id="cb34-536"></span>
<span id="cb34-537">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render_widget</span></span>
<span id="cb34-538">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> scores():</span>
<span id="cb34-539">        res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb34-540"></span>
<span id="cb34-541">        req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb34-542"></span>
<span id="cb34-543">        evals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"evals"</span>].copy()</span>
<span id="cb34-544">        evals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals.sort_values(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>)</span>
<span id="cb34-545">        evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pd.Categorical(</span>
<span id="cb34-546">            evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>], categories<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>], ordered<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb34-547">        )</span>
<span id="cb34-548"></span>
<span id="cb34-549">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># apply to the justification column</span></span>
<span id="cb34-550">        evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification_wrapped"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification"</span>].<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">apply</span>(add_line_breaks)</span>
<span id="cb34-551"></span>
<span id="cb34-552">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create a custom tooltip column</span></span>
<span id="cb34-553">        evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tooltip"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb34-554">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score: "</span></span>
<span id="cb34-555">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>].astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>)</span>
<span id="cb34-556">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;br&gt;After improvements: "</span></span>
<span id="cb34-557">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>].astype(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>)</span>
<span id="cb34-558">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;br&gt;Justification: "</span></span>
<span id="cb34-559">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"justification_wrapped"</span>]</span>
<span id="cb34-560">        )</span>
<span id="cb34-561"></span>
<span id="cb34-562">        plot <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> px.bar(</span>
<span id="cb34-563">            evals,</span>
<span id="cb34-564">            x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>,</span>
<span id="cb34-565">            y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>,</span>
<span id="cb34-566">            orientation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"h"</span>,</span>
<span id="cb34-567">            labels<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Category"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score"</span>},</span>
<span id="cb34-568">            hover_data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tooltip"</span>: <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>},  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># include the tooltip column</span></span>
<span id="cb34-569">        )</span>
<span id="cb34-570"></span>
<span id="cb34-571">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set hovertemplate to use our custom tooltip</span></span>
<span id="cb34-572">        plot.update_traces(</span>
<span id="cb34-573">            hovertemplate<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{customdata[0]}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&lt;extra&gt;&lt;/extra&gt;"</span>,</span>
<span id="cb34-574">            customdata<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tooltip"</span>]].values,</span>
<span id="cb34-575">        )</span>
<span id="cb34-576"></span>
<span id="cb34-577">        plot.update_traces(marker_color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#18bc9c"</span>)</span>
<span id="cb34-578"></span>
<span id="cb34-579">        plot.update_layout(template<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"simple_white"</span>)</span>
<span id="cb34-580"></span>
<span id="cb34-581">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> plot</span>
<span id="cb34-582"></span>
<span id="cb34-583">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.data_frame</span></span>
<span id="cb34-584">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> suggested_improvements():</span>
<span id="cb34-585">        res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb34-586"></span>
<span id="cb34-587">        req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb34-588"></span>
<span id="cb34-589">        evals <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"evals"</span>].copy()</span>
<span id="cb34-590">        evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>]</span>
<span id="cb34-591"></span>
<span id="cb34-592">        result_table <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> evals.assign(</span>
<span id="cb34-593">            Category<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"category"</span>],</span>
<span id="cb34-594">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Current score"</span>: evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score"</span>]},</span>
<span id="cb34-595">            Improvements<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"improvements"</span>],</span>
<span id="cb34-596">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score After Improvements"</span>: evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"score_after_improvements"</span>]},</span>
<span id="cb34-597">            Gain<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>evals[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>],</span>
<span id="cb34-598">        )[</span>
<span id="cb34-599">            [</span>
<span id="cb34-600">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Category"</span>,</span>
<span id="cb34-601">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Current score"</span>,</span>
<span id="cb34-602">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Improvements"</span>,</span>
<span id="cb34-603">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score After Improvements"</span>,</span>
<span id="cb34-604">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>,</span>
<span id="cb34-605">            ]</span>
<span id="cb34-606">        ].sort_values(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gain"</span>, ascending<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb34-607"></span>
<span id="cb34-608">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> result_table</span>
<span id="cb34-609"></span>
<span id="cb34-610">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb34-611">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> showtime():</span>
<span id="cb34-612">        res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb34-613"></span>
<span id="cb34-614">        req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb34-615">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'meta'</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'estimated_duration_minutes'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> minutes"</span></span>
<span id="cb34-616"></span>
<span id="cb34-617">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb34-618">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> code_savviness():</span>
<span id="cb34-619">        res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb34-620">        req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb34-621">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'meta'</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'percent_with_code'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> %"</span></span>
<span id="cb34-622"></span>
<span id="cb34-623">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb34-624">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> image_presence():</span>
<span id="cb34-625">        res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> analysis_result()</span>
<span id="cb34-626">        req(res <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb34-627">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>res[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'meta'</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'percent_with_images'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> %"</span></span>
<span id="cb34-628"></span>
<span id="cb34-629">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server)</span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-12-2" class="tab-pane" aria-labelledby="tabset-12-2-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Get this code from GitHub
</div>
</div>
<div class="callout-body-container callout-body">
<p>You can grab the code directly from <a href="https://github.com/hypebright/the-shiny-side-of-llms/blob/d1094d2774f9d0c213c7ddf6e17f94da706b1b76/R/deckcheck/app.R">here</a>.</p>
</div>
</div>
<div class="cell">
<details class="code-fold">
<summary>See full app</summary>
<div class="sourceCode cell-code" id="cb35" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb35-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(shiny)</span>
<span id="cb35-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bslib)</span>
<span id="cb35-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb35-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(mirai)</span>
<span id="cb35-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb35-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggiraph)</span>
<span id="cb35-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(gdtools)</span>
<span id="cb35-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(purrr)</span>
<span id="cb35-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)</span>
<span id="cb35-10"></span>
<span id="cb35-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Data Structure</span></span>
<span id="cb35-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reusable scoring category</span></span>
<span id="cb35-15">type_scoring_category <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb35-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb35-17">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1 to 10."</span></span>
<span id="cb35-18">  ),</span>
<span id="cb35-19">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">justification =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb35-20">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span></span>
<span id="cb35-21">  ),</span>
<span id="cb35-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb35-23">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb35-24">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb35-25">  ),</span>
<span id="cb35-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score_after_improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb35-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb35-28">  )</span>
<span id="cb35-29">)</span>
<span id="cb35-30"></span>
<span id="cb35-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Top-level deck analysis object</span></span>
<span id="cb35-32">type_deck_analysis <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb35-33">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">presentation_title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>),</span>
<span id="cb35-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_slides =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total number of slides."</span>),</span>
<span id="cb35-35">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb35-36">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing code blocks (0–100)."</span></span>
<span id="cb35-37">  ),</span>
<span id="cb35-38">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_images =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb35-39">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing images (0–100)."</span></span>
<span id="cb35-40">  ),</span>
<span id="cb35-41">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">estimated_duration_minutes =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb35-42">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated presentation length in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide."</span></span>
<span id="cb35-43">  ),</span>
<span id="cb35-44">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tone =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb35-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the presentation tone (e.g., informal, technical, playful)."</span></span>
<span id="cb35-46">  ),</span>
<span id="cb35-47">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">clarity =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-48">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb35-49">    type_scoring_category</span>
<span id="cb35-50">  ),</span>
<span id="cb35-51">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">relevance =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-52">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Asses how well the content matches the audience's background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb35-53">    type_scoring_category</span>
<span id="cb35-54">  ),</span>
<span id="cb35-55">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">visual_design =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-56">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb35-57">    type_scoring_category</span>
<span id="cb35-58">  ),</span>
<span id="cb35-59">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">engagement =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-60">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb35-61">    type_scoring_category</span>
<span id="cb35-62">  ),</span>
<span id="cb35-63">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pacing =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-64">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb35-65">    type_scoring_category</span>
<span id="cb35-66">  ),</span>
<span id="cb35-67">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">structure =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-68">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb35-69">    type_scoring_category</span>
<span id="cb35-70">  ),</span>
<span id="cb35-71">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">consistency =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-72">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb35-73">    type_scoring_category</span>
<span id="cb35-74">  ),</span>
<span id="cb35-75">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accessibility =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb35-76">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb35-77">    type_scoring_category</span>
<span id="cb35-78">  )</span>
<span id="cb35-79">)</span>
<span id="cb35-80"></span>
<span id="cb35-81"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-82"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Tool definition</span></span>
<span id="cb35-83"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-84"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb35-85"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb35-86"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb35-87"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param metric The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb35-88"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' "code" for percentage of slides containing fenced code blocks, or "images"</span></span>
<span id="cb35-89"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' for percentage of slides containing images.</span></span>
<span id="cb35-90"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return The calculated metric value.</span></span>
<span id="cb35-91">calculate_slide_metric <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(metric) {</span>
<span id="cb35-92">  html_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tempdir</span>(), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/my-presentation.html"</span>)</span>
<span id="cb35-93">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.exists</span>(html_file)) {</span>
<span id="cb35-94">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(</span>
<span id="cb35-95">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"HTML file does not exist. Please render your Quarto presentation first."</span></span>
<span id="cb35-96">    )</span>
<span id="cb35-97">  }</span>
<span id="cb35-98">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb35-99">  html_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(html_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(html_file))</span>
<span id="cb35-100"></span>
<span id="cb35-101">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb35-102">  slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strsplit</span>(html_content, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>))</span>
<span id="cb35-103"></span>
<span id="cb35-104">  total_slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(slides)</span>
<span id="cb35-105"></span>
<span id="cb35-106">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>) {</span>
<span id="cb35-107">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> total_slides</span>
<span id="cb35-108">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>) {</span>
<span id="cb35-109">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides where we see the "sourceCode" class</span></span>
<span id="cb35-110">    slides_with_code <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span>, slides))</span>
<span id="cb35-111">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb35-112">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>) {</span>
<span id="cb35-113">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides with image tag</span></span>
<span id="cb35-114">    slides_with_image <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'&lt;img'</span>, slides))</span>
<span id="cb35-115">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb35-116">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> {</span>
<span id="cb35-117">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb35-118">  }</span>
<span id="cb35-119"></span>
<span id="cb35-120">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(result)</span>
<span id="cb35-121">}</span>
<span id="cb35-122"></span>
<span id="cb35-123"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Optionally, to avoid manual work:</span></span>
<span id="cb35-124"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># create_tool_def(calculate_slide_metric)</span></span>
<span id="cb35-125">calculate_slide_metric <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb35-126">  calculate_slide_metric,</span>
<span id="cb35-127">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Returns the calculated metric value"</span>,</span>
<span id="cb35-128">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">metric =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb35-129">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'The metric to calculate: "total_slides" for total number of slides, </span></span>
<span id="cb35-130"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      "code" for percentage of slides containing fenced code blocks, or "images"</span></span>
<span id="cb35-131"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      for percentage of slides containing images.'</span>,</span>
<span id="cb35-132">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb35-133">  )</span>
<span id="cb35-134">)</span>
<span id="cb35-135"></span>
<span id="cb35-136"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-137"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Data wrangling</span></span>
<span id="cb35-138"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-139"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Convert named list to tidy data frames</span></span>
<span id="cb35-140"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb35-141"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param named_list Named list as returned by the chat</span></span>
<span id="cb35-142"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return List of two tibbles: meta and evals</span></span>
<span id="cb35-143">make_frames <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(named_list) {</span>
<span id="cb35-144">  meta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tibble</span>(</span>
<span id="cb35-145">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">presentation_title =</span> named_list<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>presentation_title,</span>
<span id="cb35-146">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_slides =</span> named_list<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>total_slides,</span>
<span id="cb35-147">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_code =</span> named_list<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>percent_with_code,</span>
<span id="cb35-148">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_images =</span> named_list<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>percent_with_images,</span>
<span id="cb35-149">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">estimated_duration_minutes =</span> named_list<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>estimated_duration_minutes,</span>
<span id="cb35-150">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tone =</span> named_list<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>tone</span>
<span id="cb35-151">  )</span>
<span id="cb35-152"></span>
<span id="cb35-153">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Evaluation sections (clarity, relevance, etc.)</span></span>
<span id="cb35-154">  eval_sections <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb35-155">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"clarity"</span>,</span>
<span id="cb35-156">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"relevance"</span>,</span>
<span id="cb35-157">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"visual_design"</span>,</span>
<span id="cb35-158">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"engagement"</span>,</span>
<span id="cb35-159">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pacing"</span>,</span>
<span id="cb35-160">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"structure"</span>,</span>
<span id="cb35-161">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"consistency"</span>,</span>
<span id="cb35-162">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"accessibility"</span></span>
<span id="cb35-163">  )</span>
<span id="cb35-164"></span>
<span id="cb35-165">  evals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">map_dfr</span>(eval_sections, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(section) {</span>
<span id="cb35-166">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as_tibble</span>(named_list[[section]][[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb35-167">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb35-168">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category =</span> section,</span>
<span id="cb35-169">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.before =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb35-170">      )</span>
<span id="cb35-171">  })</span>
<span id="cb35-172"></span>
<span id="cb35-173">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Final tidy data frame</span></span>
<span id="cb35-174">  final <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb35-175">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">meta =</span> meta,</span>
<span id="cb35-176">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">evals =</span> evals</span>
<span id="cb35-177">  )</span>
<span id="cb35-178">}</span>
<span id="cb35-179"></span>
<span id="cb35-180"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-181"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other helpers</span></span>
<span id="cb35-182"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-183"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register Monteserrat font</span></span>
<span id="cb35-184"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_gfont</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Lato"</span>)</span>
<span id="cb35-185"></span>
<span id="cb35-186"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-187"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny App</span></span>
<span id="cb35-188"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ======================</span></span>
<span id="cb35-189">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_fillable</span>(</span>
<span id="cb35-190">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Options</span></span>
<span id="cb35-191">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Busy indication is enabled by default for UI created with bslib (which we use here),</span></span>
<span id="cb35-192">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## but must be enabled otherwise with useBusyIndicators().</span></span>
<span id="cb35-193">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## useBusyIndicators(),</span></span>
<span id="cb35-194">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## General theme and styles</span></span>
<span id="cb35-195">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## 1. Bootswatch theme</span></span>
<span id="cb35-196">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bootswatch =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flatly"</span>),</span>
<span id="cb35-197">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## 2. Custom CSS</span></span>
<span id="cb35-198">  tags<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">style</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">HTML</span>(</span>
<span id="cb35-199">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb35-200"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    .bounce {</span></span>
<span id="cb35-201"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      animation: bounce 2s infinite;</span></span>
<span id="cb35-202"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    }</span></span>
<span id="cb35-203"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    @keyframes bounce {</span></span>
<span id="cb35-204"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      0%, 100% {</span></span>
<span id="cb35-205"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        transform: translateY(0);</span></span>
<span id="cb35-206"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      }</span></span>
<span id="cb35-207"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      50% {</span></span>
<span id="cb35-208"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        transform: translateY(-20px);</span></span>
<span id="cb35-209"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      }</span></span>
<span id="cb35-210"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    }</span></span>
<span id="cb35-211"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  "</span></span>
<span id="cb35-212">  )),</span>
<span id="cb35-213">  <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Layout</span></span>
<span id="cb35-214">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layout_sidebar</span>(</span>
<span id="cb35-215">    <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Sidebar content</span></span>
<span id="cb35-216">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sidebar =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sidebar</span>(</span>
<span id="cb35-217">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">400</span>,</span>
<span id="cb35-218">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Open sidebar on mobile devices and show above content</span></span>
<span id="cb35-219">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">open =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mobile =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"always-above"</span>),</span>
<span id="cb35-220">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hey, I am DeckCheck!"</span>)),</span>
<span id="cb35-221">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(</span>
<span id="cb35-222">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I can help you improve your Quarto presentations by analysing them and suggesting improvements.</span></span>
<span id="cb35-223"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      Before I can do that, I need some information about your presentation."</span></span>
<span id="cb35-224">      ),</span>
<span id="cb35-225">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fileInput</span>(</span>
<span id="cb35-226">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span>,</span>
<span id="cb35-227">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Upload your Quarto presentation"</span>,</span>
<span id="cb35-228">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accept =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".qmdx"</span>)</span>
<span id="cb35-229">      ),</span>
<span id="cb35-230">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textAreaInput</span>(</span>
<span id="cb35-231">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>,</span>
<span id="cb35-232">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"150px"</span>,</span>
<span id="cb35-233">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Describe your audience"</span>,</span>
<span id="cb35-234">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">placeholder =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb35-235">      ),</span>
<span id="cb35-236">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">numericInput</span>(</span>
<span id="cb35-237">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>,</span>
<span id="cb35-238">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Time cap for the presentation (minutes)"</span>,</span>
<span id="cb35-239">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span></span>
<span id="cb35-240">      ),</span>
<span id="cb35-241">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textInput</span>(</span>
<span id="cb35-242">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>,</span>
<span id="cb35-243">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Type of talk"</span>,</span>
<span id="cb35-244">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">placeholder =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. lightning talk, workshop, or keynote"</span></span>
<span id="cb35-245">      ),</span>
<span id="cb35-246">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textInput</span>(</span>
<span id="cb35-247">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>,</span>
<span id="cb35-248">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Event name"</span>,</span>
<span id="cb35-249">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">placeholder =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"e.g. posit::conf(2025)"</span></span>
<span id="cb35-250">      ),</span>
<span id="cb35-251">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">input_task_button</span>(</span>
<span id="cb35-252">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">id =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>,</span>
<span id="cb35-253">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> shiny<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tagList</span>(</span>
<span id="cb35-254">          bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robot"</span>),</span>
<span id="cb35-255">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyse presentation"</span></span>
<span id="cb35-256">        ),</span>
<span id="cb35-257">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label_busy =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DeckCheck is checking..."</span>,</span>
<span id="cb35-258">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"default"</span></span>
<span id="cb35-259">      )</span>
<span id="cb35-260">    ),</span>
<span id="cb35-261">    <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">## Main content</span></span>
<span id="cb35-262">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uiOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"results"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"100%"</span>)</span>
<span id="cb35-263">  )</span>
<span id="cb35-264">)</span>
<span id="cb35-265"></span>
<span id="cb35-266">server <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(input, output, session) {</span>
<span id="cb35-267">  quarto_task <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ExtendedTask<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(file_path, temp_dir) {</span>
<span id="cb35-268">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an Extended Task to avoid blocking. Note that</span></span>
<span id="cb35-269">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a temporary directory called within mirai will be</span></span>
<span id="cb35-270">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># different from the one in the "main" Shiny session. Hence,</span></span>
<span id="cb35-271">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we pass a temp_dir parameter to the task and use that.</span></span>
<span id="cb35-272">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mirai</span>(</span>
<span id="cb35-273">      {</span>
<span id="cb35-274">        qmd_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.path</span>(temp_dir, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.qmd"</span>)</span>
<span id="cb35-275">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.copy</span>(file_path, qmd_file, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">overwrite =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb35-276"></span>
<span id="cb35-277">        quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb35-278">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">input =</span> qmd_file,</span>
<span id="cb35-279">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"html"</span>)</span>
<span id="cb35-280">        )</span>
<span id="cb35-281"></span>
<span id="cb35-282">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Return the path to the markdown file</span></span>
<span id="cb35-283">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.path</span>(temp_dir, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.md"</span>)</span>
<span id="cb35-284">      },</span>
<span id="cb35-285">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Use the same environment as the Shiny app</span></span>
<span id="cb35-286">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">environment</span>()</span>
<span id="cb35-287">    )</span>
<span id="cb35-288">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-289">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bind_task_button</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb35-290"></span>
<span id="cb35-291">  chat_task <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ExtendedTask<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(</span>
<span id="cb35-292">    system_prompt,</span>
<span id="cb35-293">    markdown_content,</span>
<span id="cb35-294">    type_deck_analysis</span>
<span id="cb35-295">  ) {</span>
<span id="cb35-296">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># We're using an Extended Task to avoid blocking the session and</span></span>
<span id="cb35-297">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we start a fresh chat session each time.</span></span>
<span id="cb35-298">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># For a feedback loop, we would use a persistent chat session.</span></span>
<span id="cb35-299">    chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb35-300">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb35-301">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt,</span>
<span id="cb35-302">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span>(</span>
<span id="cb35-303">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temperature =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb35-304">      )</span>
<span id="cb35-305">    )</span>
<span id="cb35-306"></span>
<span id="cb35-307">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb35-308">    chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(calculate_slide_metric)</span>
<span id="cb35-309"></span>
<span id="cb35-310">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb35-311">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb35-312">    chat_res <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_async</span>(</span>
<span id="cb35-313">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb35-314">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb35-315">      )</span>
<span id="cb35-316">    )</span>
<span id="cb35-317"></span>
<span id="cb35-318">    chat_res<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">then</span>(<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(res) {</span>
<span id="cb35-319">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print the response from Task 1</span></span>
<span id="cb35-320">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Response from Task 1:</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb35-321">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(res, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb35-322"></span>
<span id="cb35-323">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Execute next task</span></span>
<span id="cb35-324">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb35-325">      chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_structured_async</span>(</span>
<span id="cb35-326">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb35-327">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_deck_analysis</span>
<span id="cb35-328">      )</span>
<span id="cb35-329">    })</span>
<span id="cb35-330">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-331">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bind_task_button</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit"</span>)</span>
<span id="cb35-332"></span>
<span id="cb35-333">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb35-334">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>file)</span>
<span id="cb35-335">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>audience)</span>
<span id="cb35-336">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>length)</span>
<span id="cb35-337">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>type)</span>
<span id="cb35-338">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>event)</span>
<span id="cb35-339"></span>
<span id="cb35-340">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tryCatch</span>(</span>
<span id="cb35-341">      {</span>
<span id="cb35-342">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Error for testing</span></span>
<span id="cb35-343">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># stop("This is a test error.")</span></span>
<span id="cb35-344"></span>
<span id="cb35-345">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get file path of the uploaded file</span></span>
<span id="cb35-346">        file_path <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>file<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>datapath</span>
<span id="cb35-347"></span>
<span id="cb35-348">        quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invoke</span>(file_path, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temp_dir =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tempdir</span>())</span>
<span id="cb35-349">      },</span>
<span id="cb35-350">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(e) {</span>
<span id="cb35-351">        rlang<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">warn</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(</span>
<span id="cb35-352">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Error when trying to invoke quarto_task:"</span>,</span>
<span id="cb35-353">          e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>message</span>
<span id="cb35-354">        ))</span>
<span id="cb35-355">        </span>
<span id="cb35-356">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print stack trace</span></span>
<span id="cb35-357">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(rlang<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trace_back</span>())</span>
<span id="cb35-358"></span>
<span id="cb35-359">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Show modal to the user</span></span>
<span id="cb35-360">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">showModal</span>(</span>
<span id="cb35-361">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">modalDialog</span>(</span>
<span id="cb35-362">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Oops! Something went wrong"</span>,</span>
<span id="cb35-363">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">div</span>(</span>
<span id="cb35-364">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center"</span>,</span>
<span id="cb35-365">              bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(</span>
<span id="cb35-366">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"emoji-frown-fill"</span>,</span>
<span id="cb35-367">                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2em"</span>,</span>
<span id="cb35-368">                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-warning"</span></span>
<span id="cb35-369">              ),</span>
<span id="cb35-370">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">br</span>(),</span>
<span id="cb35-371">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(</span>
<span id="cb35-372">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The not so Shiny Side of LLMs. Please check that your Quarto presentation is valid and contains slides."</span></span>
<span id="cb35-373">              )</span>
<span id="cb35-374">            ),</span>
<span id="cb35-375">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">easyClose =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb35-376">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">footer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">modalButton</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>)</span>
<span id="cb35-377">          )</span>
<span id="cb35-378">        )</span>
<span id="cb35-379">      }</span>
<span id="cb35-380">    )</span>
<span id="cb35-381">  }) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-382">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bindEvent</span>(input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>submit)</span>
<span id="cb35-383"></span>
<span id="cb35-384">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>({</span>
<span id="cb35-385">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">result</span>())</span>
<span id="cb35-386">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tryCatch</span>(</span>
<span id="cb35-387">      {</span>
<span id="cb35-388">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Error for testing</span></span>
<span id="cb35-389">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># stop("This is a test error.")</span></span>
<span id="cb35-390"></span>
<span id="cb35-391">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get the Markdown file path from the completed quarto_task</span></span>
<span id="cb35-392">        markdown_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">result</span>()</span>
<span id="cb35-393">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing the slides</span></span>
<span id="cb35-394">        markdown_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(markdown_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(markdown_file))</span>
<span id="cb35-395"></span>
<span id="cb35-396">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb35-397">        system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> here<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">here</span>(</span>
<span id="cb35-398">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompts"</span>,</span>
<span id="cb35-399">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"prompt-analyse-slides-structured-tool.md"</span></span>
<span id="cb35-400">        )</span>
<span id="cb35-401"></span>
<span id="cb35-402">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb35-403">        system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb35-404">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb35-405">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>audience,</span>
<span id="cb35-406">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>length,</span>
<span id="cb35-407">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>type,</span>
<span id="cb35-408">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> input<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>event</span>
<span id="cb35-409">        )</span>
<span id="cb35-410"></span>
<span id="cb35-411">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Trigger the chat task with the provided inputs</span></span>
<span id="cb35-412">        chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">invoke</span>(</span>
<span id="cb35-413">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt,</span>
<span id="cb35-414">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">markdown_content =</span> markdown_content,</span>
<span id="cb35-415">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type_deck_analysis =</span> type_deck_analysis</span>
<span id="cb35-416">        )</span>
<span id="cb35-417">      },</span>
<span id="cb35-418">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">error =</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(e) {</span>
<span id="cb35-419">        rlang<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">warn</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste</span>(</span>
<span id="cb35-420">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Error when trying to invoke chat_task:"</span>,</span>
<span id="cb35-421">          e<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>message</span>
<span id="cb35-422">        ))</span>
<span id="cb35-423">        </span>
<span id="cb35-424">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Print stack trace</span></span>
<span id="cb35-425">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(rlang<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">trace_back</span>())</span>
<span id="cb35-426"></span>
<span id="cb35-427">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Show modal to the user</span></span>
<span id="cb35-428">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">showModal</span>(</span>
<span id="cb35-429">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">modalDialog</span>(</span>
<span id="cb35-430">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Oops! Something went wrong"</span>,</span>
<span id="cb35-431">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">div</span>(</span>
<span id="cb35-432">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center"</span>,</span>
<span id="cb35-433">              bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(</span>
<span id="cb35-434">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"emoji-frown-fill"</span>,</span>
<span id="cb35-435">                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2em"</span>,</span>
<span id="cb35-436">                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-warning"</span></span>
<span id="cb35-437">              ),</span>
<span id="cb35-438">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">br</span>(),</span>
<span id="cb35-439">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(</span>
<span id="cb35-440">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The not so Shiny Side of LLMs. Unfortunately, chatting didn't work out. Do you have enough credits left?"</span></span>
<span id="cb35-441">              )</span>
<span id="cb35-442">            ),</span>
<span id="cb35-443">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">easyClose =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>,</span>
<span id="cb35-444">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">footer =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">modalButton</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Close"</span>)</span>
<span id="cb35-445">          )</span>
<span id="cb35-446">        )</span>
<span id="cb35-447">      }</span>
<span id="cb35-448">    )</span>
<span id="cb35-449">  })</span>
<span id="cb35-450"></span>
<span id="cb35-451">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reactive expression to hold the analysis result</span></span>
<span id="cb35-452">  analysis_result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reactive</span>({</span>
<span id="cb35-453">    named_list <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">result</span>()</span>
<span id="cb35-454"></span>
<span id="cb35-455">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">make_frames</span>(named_list)</span>
<span id="cb35-456">  })</span>
<span id="cb35-457"></span>
<span id="cb35-458">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>results <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderUI</span>({</span>
<span id="cb35-459">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (quarto_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">status</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>) {</span>
<span id="cb35-460">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">div</span>(</span>
<span id="cb35-461">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb35-462">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%;"</span>,</span>
<span id="cb35-463">        bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(</span>
<span id="cb35-464">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-slides"</span>,</span>
<span id="cb35-465">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"6em"</span>,</span>
<span id="cb35-466">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-primary bounce"</span></span>
<span id="cb35-467">        ),</span>
<span id="cb35-468">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">br</span>(),</span>
<span id="cb35-469">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Processing your Quarto presentation..."</span>)</span>
<span id="cb35-470">      )</span>
<span id="cb35-471">    } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">status</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"running"</span>) {</span>
<span id="cb35-472">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">div</span>(</span>
<span id="cb35-473">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-center d-flex flex-column justify-content-center align-items-center"</span>,</span>
<span id="cb35-474">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"height: 100%;"</span>,</span>
<span id="cb35-475">        bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(</span>
<span id="cb35-476">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"robot"</span>,</span>
<span id="cb35-477">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"6em"</span>,</span>
<span id="cb35-478">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text-primary bounce"</span></span>
<span id="cb35-479">        ),</span>
<span id="cb35-480">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">br</span>(),</span>
<span id="cb35-481">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">p</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The LLM is doing its magic..."</span>)</span>
<span id="cb35-482">      )</span>
<span id="cb35-483">    } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (chat_task<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">status</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"success"</span>) {</span>
<span id="cb35-484">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tagList</span>(</span>
<span id="cb35-485">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layout_column_wrap</span>(</span>
<span id="cb35-486">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb35-487">          <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">### Value boxes for metrics</span></span>
<span id="cb35-488">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">value_box</span>(</span>
<span id="cb35-489">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tooltip</span>(</span>
<span id="cb35-490">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">span</span>(</span>
<span id="cb35-491">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Showtime "</span>,</span>
<span id="cb35-492">                bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"question-circle-fill"</span>)</span>
<span id="cb35-493">              ),</span>
<span id="cb35-494">              <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Slides are being counted based on the provided Quarto presentation, then an educated guess is made about the time it will take to present them."</span></span>
<span id="cb35-495">            ),</span>
<span id="cb35-496">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"showtime"</span>),</span>
<span id="cb35-497">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">showcase =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-slides"</span>),</span>
<span id="cb35-498">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span></span>
<span id="cb35-499">          ),</span>
<span id="cb35-500">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">value_box</span>(</span>
<span id="cb35-501">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tooltip</span>(</span>
<span id="cb35-502">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">span</span>(</span>
<span id="cb35-503">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Code Savviness "</span>,</span>
<span id="cb35-504">                bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"question-circle-fill"</span>)</span>
<span id="cb35-505">              ),</span>
<span id="cb35-506">              <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Code Saviness is calculated based on the slides that contain code chunks. The percentage is the ratio of those slides to total slides."</span></span>
<span id="cb35-507">            ),</span>
<span id="cb35-508">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code_savviness"</span>),</span>
<span id="cb35-509">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">showcase =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-code"</span>),</span>
<span id="cb35-510">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span></span>
<span id="cb35-511">          ),</span>
<span id="cb35-512">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">value_box</span>(</span>
<span id="cb35-513">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tooltip</span>(</span>
<span id="cb35-514">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">span</span>(</span>
<span id="cb35-515">                <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Image Presence "</span>,</span>
<span id="cb35-516">                bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"question-circle-fill"</span>)</span>
<span id="cb35-517">              ),</span>
<span id="cb35-518">              <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Image Presence is calculated based on the slides that contain images. The percentage is the ratio of those slides to total slides."</span></span>
<span id="cb35-519">            ),</span>
<span id="cb35-520">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">textOutput</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"image_presence"</span>),</span>
<span id="cb35-521">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">showcase =</span> bsicons<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_icon</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file-image"</span>),</span>
<span id="cb35-522">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primary"</span></span>
<span id="cb35-523">          )</span>
<span id="cb35-524">        ),</span>
<span id="cb35-525">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">layout_column_wrap</span>(</span>
<span id="cb35-526">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>,</span>
<span id="cb35-527">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="cb35-528">          <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">### Graph with scoring metrics</span></span>
<span id="cb35-529">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb35-530">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">600</span>,</span>
<span id="cb35-531">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(</span>
<span id="cb35-532">              <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Scores per category"</span>)</span>
<span id="cb35-533">            ),</span>
<span id="cb35-534">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">girafeOutput</span>(</span>
<span id="cb35-535">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">outputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scores"</span></span>
<span id="cb35-536">            )</span>
<span id="cb35-537">          ),</span>
<span id="cb35-538">          <span class="do" style="color: #5E5E5E;
background-color: null;
font-style: italic;">### Table with suggested improvements</span></span>
<span id="cb35-539">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card</span>(</span>
<span id="cb35-540">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">600</span>,</span>
<span id="cb35-541">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">card_header</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strong</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Suggested improvements per category"</span>)),</span>
<span id="cb35-542">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tableOutput</span>(</span>
<span id="cb35-543">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">outputId =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"suggested_improvements"</span></span>
<span id="cb35-544">            )</span>
<span id="cb35-545">          )</span>
<span id="cb35-546">        )</span>
<span id="cb35-547">      )</span>
<span id="cb35-548">    }</span>
<span id="cb35-549">  })</span>
<span id="cb35-550"></span>
<span id="cb35-551">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>scores <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderGirafe</span>({</span>
<span id="cb35-552">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb35-553"></span>
<span id="cb35-554">    evals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>evals</span>
<span id="cb35-555"></span>
<span id="cb35-556">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Order by score</span></span>
<span id="cb35-557">    data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> evals <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-558">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(score) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-559">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb35-560">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(category, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">levels =</span> category),</span>
<span id="cb35-561">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tooltip =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb35-562">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score: "</span>,</span>
<span id="cb35-563">          score,</span>
<span id="cb35-564">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb35-565">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"After improvements: "</span>,</span>
<span id="cb35-566">          score_after_improvements,</span>
<span id="cb35-567">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb35-568">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Justification: "</span>,</span>
<span id="cb35-569">          justification</span>
<span id="cb35-570">        )</span>
<span id="cb35-571">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-572">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(category, score, score_after_improvements, tooltip)</span>
<span id="cb35-573"></span>
<span id="cb35-574">    p <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(</span>
<span id="cb35-575">      data,</span>
<span id="cb35-576">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> category, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> score, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tooltip =</span> tooltip, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data_id =</span> category)</span>
<span id="cb35-577">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb35-578">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_bar_interactive</span>(</span>
<span id="cb35-579">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stat =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"identity"</span>,</span>
<span id="cb35-580">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#18bc9c"</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Success color of Flatly theme</span></span>
<span id="cb35-581">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb35-582">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(</span>
<span id="cb35-583">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Category"</span>,</span>
<span id="cb35-584">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score"</span></span>
<span id="cb35-585">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb35-586">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Flip to make horizontal bar chart</span></span>
<span id="cb35-587">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coord_flip</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb35-588">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_family =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Lato"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb35-589">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>)</span>
<span id="cb35-590"></span>
<span id="cb35-591">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">girafe</span>(</span>
<span id="cb35-592">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ggobj =</span> p,</span>
<span id="cb35-593">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">options =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb35-594">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_selection</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>),</span>
<span id="cb35-595">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_sizing</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rescale =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>),</span>
<span id="cb35-596">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_tooltip</span>(</span>
<span id="cb35-597">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">css =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background-color: #f0f0f0; color: #333; padding: 5px; border-radius: 5px; width: 200px;"</span></span>
<span id="cb35-598">        ),</span>
<span id="cb35-599">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_hover</span>(</span>
<span id="cb35-600">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">css =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"."</span></span>
<span id="cb35-601">        ),</span>
<span id="cb35-602">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_hover_inv</span>(</span>
<span id="cb35-603">          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">css =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"opacity: 0.5;"</span></span>
<span id="cb35-604">        )</span>
<span id="cb35-605">      )</span>
<span id="cb35-606">    )</span>
<span id="cb35-607">  })</span>
<span id="cb35-608"></span>
<span id="cb35-609">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>suggested_improvements <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderTable</span>({</span>
<span id="cb35-610">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb35-611"></span>
<span id="cb35-612">    evals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>evals</span>
<span id="cb35-613"></span>
<span id="cb35-614">    evals <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-615">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(score) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-616">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(</span>
<span id="cb35-617">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Gain =</span> score_after_improvements <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> score</span>
<span id="cb35-618">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-619">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">select</span>(</span>
<span id="cb35-620">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Category =</span> category,</span>
<span id="cb35-621">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Current score</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> score,</span>
<span id="cb35-622">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Improvements =</span> improvements,</span>
<span id="cb35-623">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Score After Improvements</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> score_after_improvements,</span>
<span id="cb35-624">        Gain</span>
<span id="cb35-625">      ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb35-626">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">desc</span>(Gain))</span>
<span id="cb35-627">  })</span>
<span id="cb35-628"></span>
<span id="cb35-629">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Update value boxes based on analysis_result()$meta</span></span>
<span id="cb35-630">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>showtime <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderText</span>({</span>
<span id="cb35-631">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb35-632">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(</span>
<span id="cb35-633">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>meta<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>estimated_duration_minutes,</span>
<span id="cb35-634">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" minutes"</span></span>
<span id="cb35-635">    )</span>
<span id="cb35-636">  })</span>
<span id="cb35-637"></span>
<span id="cb35-638">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>code_savviness <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderText</span>({</span>
<span id="cb35-639">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb35-640">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>meta<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>percent_with_code, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" %"</span>)</span>
<span id="cb35-641">  })</span>
<span id="cb35-642"></span>
<span id="cb35-643">  output<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>image_presence <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">renderText</span>({</span>
<span id="cb35-644">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">req</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>())</span>
<span id="cb35-645">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">analysis_result</span>()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>meta<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>percent_with_images, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" %"</span>)</span>
<span id="cb35-646">  })</span>
<span id="cb35-647">}</span>
<span id="cb35-648"></span>
<span id="cb35-649"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shinyApp</span>(ui, server)</span></code></pre></div>
</details>
</div>
</div>
</div>
</div>
</section>
<section id="ready-for-the-world-deployment" class="level1">
<h1>Ready for the world: deployment</h1>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<p>There are various ways to make your Shiny app available to a wider audience via the web. Posit offers the following solutions:</p>
<ul>
<li><a href="https://connect.posit.cloud/">Posit Connect Cloud</a> (cloud hosting, free and paid plans)</li>
<li><a href="http://shinyapps.io">shinyapps.io</a> (cloud hosting, free and paid plans)</li>
<li><a href="https://rstudio.com/products/shiny/shiny-server/">Shiny Server</a> (self-hosted, free and open source)</li>
<li><a href="https://www.rstudio.com/products/connect/">Posit Connect</a> (self-hosted, professional plans)</li>
</ul>
</div>
</div>
<p>We didn’t develop DeckCheck all for ourselves: we want to help every presenting Data Scientist with a polished presentation! So it’s time to put our Shiny app on the web. Luckily, there are a couple of main ways to do it:</p>
<ol type="1">
<li><strong>Cloud hosting:</strong> the “click-and-play” option</li>
<li><strong>Self-hosted deployments:</strong> the “I like control and flexibility” option</li>
</ol>
<p>Cloud hosting is perfect if you just want your app to be live and don’t feel like babysitting a server (although babysitting can be fun!). A good place to start is <a href="https://connect.posit.cloud/">Posit Connect Cloud</a> or <a href="https://www.shinyapps.io/">shinyapps.io</a>. You push your code, and in a few clicks, your app has its own URL and is accessible to anyone with an internet connection. It’s fast, convenient, and you can scale if traffic grows.</p>
<p>Self-hosted give you full control and flexibility. <a href="https://rstudio.com/products/shiny/shiny-server/">Shiny Server</a> is a free and open source solution that lets you run Python or R Shiny apps in a controlled environment (e.g.&nbsp;on your own server). You decide when and how updates happen, how many users can connect, and whether to open the app to the public or keep it behind the corporate firewall. <a href="https://www.rstudio.com/products/connect/">Posit Connect</a> takes that control a step further and wraps it in a professional enterprise solution: scheduling, user authentication, email notifications, and support for Shiny, FastAPI, Plumber, Quarto, and other popular Python and R frameworks.</p>
<p>In short: click-and-play = fast, simple, and ready to share. I-like-control = more setup, full flexibility, and enterprise-ready features. Either way, your Shiny app is ready for the world! And of course, beyond what’s mentioned here, there also many other solutions at any cloud service provider (Azure, AWS, GCP…).</p>
</section>
<section id="every-series-has-its-shiny-ending" class="level1">
<h1>Every series has its Shiny ending</h1>
<p>That’s the end of “The Shiny Side of LLMs”! Over three parts, you learned what an LLM actually is (and what it definitely isn’t), how to have a conversation with one using <code>chatlas</code> (Python) and <code>ellmer</code> (R), and how to make your ideas come to life in a Shiny app. Need some inspiration? Check out the <a href="https://shiny.posit.co/py/docs/genai-inspiration.html">Generative AI docs for Python</a> or the examples in the <a href="https://posit-dev.github.io/shinychat/r/index.html"><code>shinychat</code> docs for R</a>.</p>
<p>I hope this series showed you the shiny side of LLMs, beyond the hype. My goal with this series was to make LLMs feel a little less like mysterious black boxes and a little more like tools you can actually use. Hopefully you’ve picked up a few tricks and now feel ready to try them out in your own work. I’m curious to see what you’ll create! Feel free to stay in touch via <a href="https://www.linkedin.com/in/veerlevanleemput/">LinkedIn</a>.</p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/</guid>
  <pubDate>Mon, 15 Sep 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-3/shiny-side-of-llms-header.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Shiny at posit::conf(2025)</title>
  <dc:creator>Shiny Team</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-at-conf-2025/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-at-conf-2025/shiny-team.jpg" alt="Shiny team photo from SciPy 2025" style="width: 100%; height: auto;">
    </div>
    


<div class="{lead}">
<p>There are many talks and workshops about Shiny by the community and Shiny team at posit::conf(2025). We’d love to chat and say hello!</p>
</div>
<section id="talks-by-posit-folks" class="level2">
<h2 data-anchor-id="talks-by-posit-folks">Talks by Posit Folks</h2>
<div id="listing-talks" class="quarto-listing quarto-listing-container-custom">
<div class="grid">
  
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1732722180041001iVBe" class="text-decoration-none" target="_blank">
                    Programming with LLM APIs: A Beginner's Guide in R and Python
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Joe Cheng, Garrick Aden-Buie
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Workshop
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Tue, Sep 16
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      9:00 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1732722180041001iVBe" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                  <a href="https://posit-conf-2025.github.io/llm/" class="btn btn-primary btn-sm" target="_blank">
                    <i class="bi bi-file-earmark-slides me-1"></i>
                    View Slides
                  </a>
                  
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1734707407405001PB1c" class="text-decoration-none" target="_blank">
                    Shiny for Python
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Daniel Chen
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Workshop
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Tue, Sep 16
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      9:00 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Chicago E
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1734707407405001PB1c" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1733331481838001OnxY" class="text-decoration-none" target="_blank">
                    Shiny for R
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Colin Rundel
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Workshop
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Tue, Sep 16
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      9:00 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Hanover D
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1733331481838001OnxY" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="text-decoration-none" target="_blank">
                    Modular, layout-as-code approach for customizable Shiny dashboards
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Kim Schouten
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny for Humans
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      10:20 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="text-decoration-none" target="_blank">
                    shinyGovStyle - accessible government design in RShiny
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Cameron Race
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny for Humans
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      10:40 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="text-decoration-none" target="_blank">
                    Death by Dropdown? Engineer Insightful Shiny Apps with Behavioral Science
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Jeremy Winget, PhD
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny for Humans
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      11:00 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="text-decoration-none" target="_blank">
                    Design of Everyday Shiny Apps
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Casey Aguilar-Gervase, Maya Gans
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny for Humans
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      11:20 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602179001akB6" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="text-decoration-none" target="_blank">
                    Old Apps, New Tricks: How AI can write Automated Tests for your Shiny Apps
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Karan Gathani
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny New Tricks
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      1:00 PM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                  <a href="https://docs.google.com/presentation/d/130bJNGp3XIlKPaouA8K0Lv0O3P7OFesW/edit?usp=sharing&amp;ouid=105060716153365836674" class="btn btn-primary btn-sm" target="_blank">
                    <i class="bi bi-file-earmark-slides me-1"></i>
                    View Slides
                  </a>
                  
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="text-decoration-none" target="_blank">
                    Web applications with Shiny and React (and AI)
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Winston Chang
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny New Tricks
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      1:20 PM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="text-decoration-none" target="_blank">
                    Observability at scale: Monitoring Shiny Applications with OpenTelemetry
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Barret Schloerke
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny New Tricks
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      1:40 PM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                  <a href="https://github.com/schloerke/presentation-2025-09-17-posit-conf-otel" class="btn btn-primary btn-sm" target="_blank">
                    <i class="bi bi-file-earmark-slides me-1"></i>
                    View Slides
                  </a>
                  
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="text-decoration-none" target="_blank">
                    shinystate: Launching collaboration and session management to new heights in Shiny applications
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Eric Nantz
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      Shiny New Tricks
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Wed, Sep 17
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      2:20 PM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351602260001az1A" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351601272001atTE" class="text-decoration-none" target="_blank">
                    Disposable Shiny Apps
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      James Wade
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      The Power of Presentation
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Thu, Sep 18
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      10:20 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351601272001atTE" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351601272001atTE" class="text-decoration-none" target="_blank">
                    Talk data to me: How to present youR data to any audience
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Freda Warner
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      The Power of Presentation
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Thu, Sep 18
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      11:00 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351601272001atTE" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351601272001atTE" class="text-decoration-none" target="_blank">
                    Theming Made Easy: Introducing brand.yml
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="mb-2">
                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-person-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Speaker:</span>
                      Garrick Aden-Buie
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-diagram-3-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Track:</span>
                      The Power of Presentation
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-calendar-event-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Date</span>
                      Thu, Sep 18
                    </div>
                  </div>

                  <div class="d-flex align-items-start mb-2">
                    <div class="me-2">
                      <i class="bi bi-clock-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Time</span>
                      11:20 AM
                    </div>
                  </div>

                  <div class="d-flex align-items-start">
                    <div class="me-2">
                      <i class="bi bi-geo-alt-fill"></i>
                    </div>
                    <div>
                      <span class="fw-bold">Room:</span>
                      Regency VII
                    </div>
                  </div>
                </div>
              </div>

            </div>
          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://reg.rainfocus.com/flow/posit/positconf25/attendee-portal/page/sessioncatalog/session/1745351601272001atTE" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                Conf Talk Page
              </a>
              

                
                    <button class="btn btn-outline-secondary btn-sm" disabled="">
                      <i class="bi bi-file-earmark-slides me-1"></i>
                      Slides Coming Soon
                    </button>
                    
          </div>
        </div>
      </div>
    </div>
    
</div>

<div class="listing-no-matching d-none">
No matching items
</div>
</div>



</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-at-conf-2025/</guid>
  <pubDate>Fri, 12 Sep 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-at-conf-2025/shiny-team.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Talking to LLMs: From Prompt to Response</title>
  <dc:creator>Veerle Eeftink - van Leemput</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/shiny-side-of-llms-header.png" alt="The Shiny Side of LLMs part 2" style="width: 100%; height: auto;">
    </div>
    


<p>Welcome back to ”The Shiny Side of LLMs”: a blog series for Python and R users who want to build real, useful LLM-powered apps without getting buried in jargon or deep learning theory.</p>
<p>In <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/">Part 1: What LLMs Actually Do (and What They Don’t)</a>, we looked at how large language models generate responses, why they sometimes seem so smart (and other times so confidently wrong), and what kinds of tasks they’re actually good at. Now it’s time to get hands-on!</p>
<p>In this part, we’ll focus on what it means to talk to an LLM. We’ll cover crafting prompts to parsing responses, and everything in between. You’ll learn:</p>
<ul>
<li>How to call an LLM via <a href="https://posit-dev.github.io/chatlas/"><code>chatlas</code></a> (Python) and <a href="https://ellmer.tidyverse.org/"><code>ellmer</code></a> (R)</li>
<li>The basics of prompt design: roles, instructions, system messages, temperature, and more</li>
<li>What goes in and what comes out, and how your own data fits into that loop</li>
<li>How to work with model outputs: parsing, structuring, and getting predictable results</li>
<li>How to give LLMs extra power with tools</li>
<li>Costs involved when talking to LLMs</li>
</ul>
<p>By the end of this part, you’ll be ready to go from “I asked ChatGPT a thing” to “I can programmatically talk to an LLM by sending custom prompts and doing something useful with the results”.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
No time for the walkthrough?
</div>
</div>
<div class="callout-body-container callout-body">
<p>Jump straight to the full workflow.</p>
</div>
</div>
<section id="the-app-deckcheck" class="level1">
<h1>The app: DeckCheck</h1>
<p>In this series we’re working towards a genius app that gets rid of lengthy unfocused presentations, like a perfect “Presentation Rehearsal Buddy”. No app without a snappy name, so hello “DeckCheck”. The goal: let users upload their Quarto presentation and provide them with feedback on how to make it better.</p>
<p>So how would we go about that? To give meaningful feedback on a presentation, we need to ask the LLM the right questions. For example:</p>
<ul>
<li>Does the presentation have a clear structure?</li>
<li>Are the key messages easy to spot?</li>
<li>Does the tone match the intended audience?</li>
<li>Are the slides too wordy, too vague, or just right?</li>
<li>Is there a good balance between words, visuals, and code?</li>
</ul>
<p>If you’ve used ChatGPT or another LLM in a chat interface, the process feels straightforward: you ask any of the above questions, and the model replies. But when you’re building an app, you’re not just having a conversation. You want to have some form of control. After all, you’re designing a system. You need to construct prompts carefully, handle responses programmatically, and ensure the whole exchange is stable enough to support user-facing features. In our case, that means feeding slide content into the LLM and asking targeted questions about structure, clarity, and tone. And we’re not just hoping for a helpful paragraph with some witty comments. For our app we need output that’s predictable, parseable, and ready to plug into our app logic. Eventually we want to be able to extract suggestions, metrics, scores: things that we also need to retrieve programmatically.</p>
<p>So in this part, it’s not just about asking questions, it’s about:</p>
<ul>
<li>How to design prompts that get reliable, structured answers</li>
<li>How to “control” model behaviour (as far as you can, at least)</li>
<li>How to send slide content (or any other data) to an LLM</li>
<li>And how to process the model’s output into something our future app can actually use</li>
</ul>
<p>Let’s build!</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
What can you expect from this second part?
</div>
</div>
<div class="callout-body-container callout-body">
<p>This part focusses on programmatically talking to an LLM. We leave the Shiny app building to the third and last part of “The Shiny Side of LLMs” series. The code we end up with in this part serves as a basis for the back-end of our app in this next part.</p>
</div>
</div>
</section>
<section id="what-do-you-need" class="level1">
<h1>What do you need?</h1>
<p>Ah, is it even a tutorial if there isn’t a “getting started” step? But there’s one obvious thing we need: access to an LLM. To get that, you’ll need to sign up for a (developer) account with a provider like OpenAI or Anthropic. And with that comes something that feels like a huge barrier: setting up payment details. I feel you when you might be getting anxious about an invoice with a lot of zeros, but really, don’t worry too much about that. Almost all platforms offer free trial credits, and you can usually set usage limits or buy fixed prepaid credits (Anthropic makes this especially easy). So that invoice-nightmare won’t come alive easily. Still a little bit worried? Google’s model Gemini offers a generous free tier.</p>
<p>Once you’ve registered, you can get an API key. You need this key to authenticate with the LLM provider. Never, ever hardcode the key directly into your script. You’ll be amazed how many keys are publicly available on GitHub repos. Don’t be that developer. As always with secrets, store it as an environment variable. Just note that the exact name of the key depends on the provider. For example, Anthropic expects <code>ANTHROPIC_API_KEY=yourkey</code>, while OpenAI uses <code>OPENAI_API_KEY=yourkey</code>.</p>
<p>To keep things simple, we’ll go with Anthropic for the examples in this blog series, but feel free to choose any provider you want. Don’t worry about lock-in either: you’ll see that switching providers and models is super easy.</p>
<section id="where-to-store-your-api-key" class="level2">
<h2 class="anchored" data-anchor-id="where-to-store-your-api-key">Where to store your API key</h2>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<p>In Python, the recommended approach is to create a <code>.env</code> file in your project folder:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1">ANTHROPIC_API_KEY<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>yourkey</span></code></pre></div>
<p>It’s recommended to use the <code>dotenv</code> package to load the <code>.env</code> file into your environment:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb2-2"></span>
<span id="cb2-3">load_dotenv()</span></code></pre></div>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<p>In R, environment variables are typically stored in a <code>.Renviron</code> file. You can create this file in your project root or in your home directory (<code>~/.Renviron</code>). Or, if you want to make it yourself really easy: you can also open/edit the relevant file with <code>usethis::edit_r_environ()</code>.</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">ANTHROPIC_API_KEY<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span>yourkey</span></code></pre></div>
</div>
</div>
</div>
<p>Just be sure to add <code>.env</code> and <code>.Renviron</code> to your <code>.gitignore</code> file so you don’t accidentally publish your keys.</p>
</section>
<section id="installing-chatlas-or-ellmer" class="level2">
<h2 class="anchored" data-anchor-id="installing-chatlas-or-ellmer">Installing <code>chatlas</code> or <code>ellmer</code></h2>
<p>Working with different LLM providers can be a pain, but luckily there is the perfect solution: <a href="https://posit-dev.github.io/chatlas/"><code>chatlas</code></a> for Python and <a href="https://ellmer.tidyverse.org"><code>ellmer</code></a> for R. Hello simple and consistent interface! From Anthropic’s Claude to (Azure) OpenAI and Google Gemini: it’s all possible. And bonus: <code>chatlas</code> and <code>ellmer</code> both have the same interface, making it easy to translate your LLM projects between Python and R!</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-2-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-1" aria-controls="tabset-2-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-2-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-2" aria-controls="tabset-2-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-2-1" class="tab-pane active" aria-labelledby="tabset-2-1-tab">
<p>For Python, <code>chatlas</code> is available on PyPI, so you can install it easily with <code>pip</code>:</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install chatlas</span></code></pre></div>
<p>Or, if you’re using <code>uv</code>, add it like so:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">uv</span> add chatlas</span></code></pre></div>
<p>Once installed, import it like this:</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> chatlas</span></code></pre></div>
<p>Depending on the LLM provider you will choose, you might need to install additional packages (e.g.&nbsp;<code>anthropic</code> for, surprise, models from Anthropic). For every provider, the relevant function reference page in the <a href="https://posit-dev.github.io/chatlas/reference/"><code>chatlas</code> docs</a> will tell you what’s necessary together with credential acquisition tips (e.g.&nbsp;<a href="https://posit-dev.github.io/chatlas/reference/ChatAnthropic.html"><code>ChatAnthropic</code></a>).</p>
</div>
<div id="tabset-2-2" class="tab-pane" aria-labelledby="tabset-2-2-tab">
<p>You can install <code>ellmer</code> from CRAN using:</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ellmer"</span>)</span></code></pre></div>
<p>Once installed, load the package as usual:</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span></code></pre></div>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Switching between Python and R
</div>
</div>
<div class="callout-body-container callout-body">
<p>Did you know you can very easily switch between Python and R in <a href="https://positron.posit.co">Positron</a>? Just select the interpreter you want (you can even choose between Python and R installs), and you’re good to go!</p>
</div>
</div>
</section>
</section>
<section id="your-first-conversation" class="level1">
<h1>Your first conversation</h1>
<p>Time to chat! Whether you’re using Python or R, the workflow is the same. First, you need to create a chat object. You can give it any name you like, but <code>chat</code> seems the most obvious choice here. This <code>chat</code> object is created with a specific function for your chosen provider, where you (optionally) can specify the model. While the <code>model</code> argument isn’t required, it is strongly recommended you do specify it. After all, we’re looking for consistent behaviour in our Shiny app, and a change in default model choice isn’t going to give us that. In our case we will go for claude-sonnet-4-20250514 from <a href="https://docs.anthropic.com/en/docs/about-claude/models/overview">Anthropic</a>, which is the latest (reasonably priced) model at the time of writing.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-3-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-1" aria-controls="tabset-3-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-3-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-2" aria-controls="tabset-3-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-3-1" class="tab-pane active" aria-labelledby="tabset-3-1-tab">
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb9-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic</span>
<span id="cb9-3"></span>
<span id="cb9-4">load_dotenv()  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Loads key from the .env file</span></span>
<span id="cb9-5"></span>
<span id="cb9-6">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>)</span></code></pre></div>
</div>
<div id="tabset-3-2" class="tab-pane" aria-labelledby="tabset-3-2-tab">
<div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb10-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb10-2"></span>
<span id="cb10-3">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb10-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span></span>
<span id="cb10-5">)</span></code></pre></div>
<p>In <code>ellmer</code> 0.3.0 you can also use <code>chat()</code> using a string like <code>chat("anthropic")</code> or <code>chat("openai/gpt-4.1-nano")</code>.</p>
<p>Not sure what models are available? You can use <code>models_*()</code>, e.g.&nbsp;<code>models_anthropic()</code>, to get a list of available models.</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
API keys and environment variables
</div>
</div>
<div class="callout-body-container callout-body">
<p>There’s no need to explicitly provide your API key to <code>chatlas</code> or <code>ellmer</code>. If you specify it with the correct name in your environment file (<code>.env</code> or <code>.Renviron</code>), <code>chatlas</code> or <code>ellmer</code> will automatically find it. There’s an <code>api_key</code> argument, but generally you won’t use this. For Python, it’s recommended to use the <code>dotenv</code> package to load the <code>.env</code> file into your environment.</p>
</div>
</div>
<p>You can call the <code>chat</code> method on the <code>chat</code> object. When you use the <code>chat</code> method:</p>
<ul>
<li>You see the response appear in your console, piece by piece, like it’s being typed out. That’s the streaming part.</li>
<li>The full message is also (invisibly) saved as a character vector, so you can use it in your code later.</li>
</ul>
<p>For simplicity, let’s start with our very basic first prompt: ”I’m working on a presentation with the title: ‘The Shiny Side of LLMs’. What’s your feedback just based on that title?”</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-4-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-1" aria-controls="tabset-4-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-4-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-2" aria-controls="tabset-4-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-4-1" class="tab-pane active" aria-labelledby="tabset-4-1-tab">
<div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb11-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic</span>
<span id="cb11-3"></span>
<span id="cb11-4">load_dotenv()  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Loads key from the .env file</span></span>
<span id="cb11-5"></span>
<span id="cb11-6">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>)</span>
<span id="cb11-7"></span>
<span id="cb11-8">chat.chat(</span>
<span id="cb11-9">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I'm working on a presentation with the title: 'The Shiny Side of LLMs'. </span></span>
<span id="cb11-10"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">  What's your feedback just based on that title?"</span></span>
<span id="cb11-11">)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb12-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">That's a clever and engaging title! Here's my feedback:                                           </span></span>
<span id="cb12-3"></span>
<span id="cb12-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">What works well:                                                                                            </span></span>
<span id="cb12-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • The wordplay on "shiny" is effective - it suggests both the exciting/promising aspects of LLMs and has a </span></span>
<span id="cb12-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  nice tech-forward feel                                                                                   </span></span>
<span id="cb12-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • It's concise and memorable                                                                               </span></span>
<span id="cb12-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • Sets an optimistic, forward-looking tone for your presentation                                           </span></span>
<span id="cb12-9"></span>
<span id="cb12-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Potential considerations:                                                                         </span></span>
<span id="cb12-11"></span>
<span id="cb12-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • The phrase "shiny side" might evoke "bright side" or even "dark side," which could work for or against   </span></span>
<span id="cb12-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  you depending on your audience's associations                                                            </span></span>
<span id="cb12-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • It's somewhat informal - great for most audiences, but consider if your context requires more formal     </span></span>
<span id="cb12-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  language                                                                                                 </span></span>
<span id="cb12-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • Without additional context, it's not immediately clear what specific aspects you'll cover (capabilities, </span></span>
<span id="cb12-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  applications, benefits, etc.)                                                                            </span></span>
<span id="cb12-18"></span>
<span id="cb12-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Questions to consider:                                                                            </span></span>
<span id="cb12-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  </span></span>
<span id="cb12-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • Who's your audience? (Technical experts, business leaders, general public?)                              </span></span>
<span id="cb12-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • Are you contrasting with challenges/limitations, or purely focusing on positives?                        </span></span>
<span id="cb12-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  • What's the main takeaway you want people to remember?                                                    </span></span>
<span id="cb12-24"></span>
<span id="cb12-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Overall, it's an attention-grabbing title that should work well for most presentations about LLM benefits   </span></span>
<span id="cb12-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">and capabilities. You might consider a subtitle if you want to hint at your specific focus areas. </span></span>
<span id="cb12-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-4-2" class="tab-pane" aria-labelledby="tabset-4-2-tab">
<div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb13-2"></span>
<span id="cb13-3">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb13-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span></span>
<span id="cb13-5">)</span>
<span id="cb13-6"></span>
<span id="cb13-7">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(</span>
<span id="cb13-8">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I'm working on a presentation with the title: 'The Shiny Side of LLMs'. </span></span>
<span id="cb13-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  What's your feedback just based on that title?"</span></span>
<span id="cb13-10">)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; That's a clever and engaging title! Here's my feedback:</span></span>
<span id="cb14-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; **Strengths:**</span></span>
<span id="cb14-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - **Memorable wordplay** – "shiny" is a nice play on both the positive aspects and the tech/AI aesthetic  </span></span>
<span id="cb14-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - **Clear focus** – Immediately signals you're highlighting the benefits/successes rather than risks or limitations  </span></span>
<span id="cb14-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - **Accessible language** – Avoids jargon while still being specific to your audience  </span></span>
<span id="cb14-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; **Considerations:**</span></span>
<span id="cb14-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - **Audience expectations** – The playful tone suggests this might be more of an overview/promotional presentation rather than a deep technical dive. Make sure that aligns with your goals.  </span></span>
<span id="cb14-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - **Balance question** – Will you acknowledge any limitations, or is this specifically meant to be a positive-focused presentation? The title suggests the latter.  </span></span>
<span id="cb14-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; **Potential alternatives** (if you want to explore):  </span></span>
<span id="cb14-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - "LLMs: The Bright Side of AI"  </span></span>
<span id="cb14-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - "When LLMs Shine: Success Stories and Breakthroughs"  </span></span>
<span id="cb14-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; - "The Promise of LLMs: Real-World Wins"  </span></span>
<span id="cb14-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; But honestly, your original title works well – it's catchy and sets clear expectations. The success will depend on whether your content delivers on that promise of showcasing genuinely impressive LLM capabilities and applications.  </span></span>
<span id="cb14-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; What's the context/audience for this presentation?</span></span></code></pre></div>
</details>
</div>
</div>
</div>
</div>
<p>The <code>chat</code> object is a stateful object, which means it “remembers stuff”. It accumulates conversation history by default. This history is provided to the LLM with every subsequent question. So if you send a second question, it will package your prompt so it contains the first question, the first answer, and your second question. This is desired behaviour for multi-turn conversations since it allows the model to “remember” previous interactions. That’s important, because out of the box, the model doesn’t remember what you said two messages ago. It only has the input you provide, right here, right now. That’s its entire world. So every time (with every request) you have to remind the LLM of prior context. You can learn more about this in <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/">the first part of this series</a>.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Some chat terminology
</div>
</div>
<div class="callout-body-container callout-body">
<p>Let’s get some terms straight: you (the user) have a chat with the LLM (the assistant). Every chat, aka conversation, consists of pairs of user and assistant turns. The questions you ask correspond to an HTTP request, and the response that gets returned corresponds to an HTTP response.</p>
</div>
</div>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-5-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-1" aria-controls="tabset-5-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-5-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-2" aria-controls="tabset-5-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-5-1" class="tab-pane active" aria-labelledby="tabset-5-1-tab">
<div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> dotenv <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> load_dotenv</span>
<span id="cb15-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic</span>
<span id="cb15-3"></span>
<span id="cb15-4">load_dotenv()  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Loads key from the .env file</span></span>
<span id="cb15-5"></span>
<span id="cb15-6">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>)</span>
<span id="cb15-7"></span>
<span id="cb15-8">chat.chat(</span>
<span id="cb15-9">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I'm working on a presentation with the title: 'The Shiny Side of LLMs'. </span></span>
<span id="cb15-10"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">    What's your feedback just based on that title?"</span></span>
<span id="cb15-11">)</span>
<span id="cb15-12">chat.chat(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"What is my presentation title?"</span>)</span></code></pre></div>
<div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb16-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Your presentation title is: "The Shiny Side of LLMs"</span></span>
<span id="cb16-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
<p>In the remainder of this article, we will leave the <code>dotenv</code> part out for brevity. You do need it for the <code>.env</code> approach, but there are also other ways to load environment variables, like setting them in your bash/zsh profile.</p>
</div>
<div id="tabset-5-2" class="tab-pane" aria-labelledby="tabset-5-2-tab">
<div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb17-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb17-2"></span>
<span id="cb17-3">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb17-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span></span>
<span id="cb17-5">)</span>
<span id="cb17-6"></span>
<span id="cb17-7">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(</span>
<span id="cb17-8">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I'm working on a presentation with the title: 'The Shiny Side of LLMs'. </span></span>
<span id="cb17-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  What's your feedback just based on that title?"</span></span>
<span id="cb17-10">)</span>
<span id="cb17-11"></span>
<span id="cb17-12">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"What is my presentation title?"</span>)</span></code></pre></div>
<div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb18-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Your presentation title is: "The Shiny Side of LLMs"</span></span></code></pre></div>
</div>
</div>
</div>
</section>
<section id="designing-effective-prompts" class="level1">
<h1>Designing effective prompts</h1>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<p>Defining the <strong>role</strong>, <strong>task</strong>, <strong>context</strong>, and <strong>output format</strong> sets the foundation for a good conversation with the model.</p>
</div>
</div>
<p>In our first conversation, our prompt (”I’m working on a presentation with the title: ‘The Shiny Side of LLMs’. What’s your feedback just based on that title?”) is very general and leads to some issues: the model, Claude, doesn’t know who the audience is, what kind of feedback you’re looking for (tone, clarity, technical depth?), or what role it should take. As a result, the response is polite and somewhat helpful, but not really focused or consistent. It’s basically guessing, and it will guess something different every single time. Our prompt is very important for the model’s output, so we need to make it better by including:</p>
<ul>
<li><strong>Role</strong>: who the model is acting as. We didn’t tell the model who it should be or how it should respond. Saying something like: “You are a presentation coach for data scientists”, helps to get more relevant feedback. It also referred to as adding “colour” to your prompt, or framing.</li>
<li><strong>Task</strong>: what the model should do. We asked for “feedback”, but that’s not explicit at all. It’s better to ask something like: “Evaluate the title’s clarity, tone, and relevance for a non-technical audience”.</li>
<li><strong>Context</strong>: a bit more information about the situation. The more a model knows, the more on-point an answer will be. Who’s our intended audience? Is it a keynote, a lightning talk or a workshop? In our to-be app, we can get this information directly from the user when they are uploading their Quarto slides, and include it in our prompt. It’s as simple as adding: “This is for a 10-minute lightning talk at <a href="https://posit.co/conference/">posit::conf(2025)</a> for people curious about AI”. How much context (aka tokens) you can add is dependent on the model. Claude Sonnet 4 recently <a href="https://www.anthropic.com/news/1m-context">supports 1M tokens of context</a>, which really is a lot! Context is also important for refusal protection: if there’s not enough context, sometimes the model refuses to answer. Giving clear background and reassuring details helps avoid these unnecessary refusals, making the interaction smoother and more reliable. Of course, keep in mind that you are responsible the model’s output: sometimes refusals are there for a reason.</li>
<li><strong>Output format</strong>: how you want the answer. If you want a list, markdown, a table, or a short paragraph, say so! You can (and should) be very detailed here. For example: “Return your answer as a JSON array of objects with keys ‘aspect’ and ‘feedback’”. Choose something that’s easily parsable in Python or R later. Also remember the few-shot learning we talked about in Part 1: if you provide an example of what your output should look like, it’s easier for the model to repeat that pattern.</li>
</ul>
<p>Up to this point we’ve talked about adding role, task, context, and output format directly into “the” prompt. Like it’s just one big thing. But there are actually two kinds of prompts you can use when chatting with a model: the system prompt and the user prompt.</p>
<ul>
<li>The <strong>system prompt</strong> is where you set the ground rules: it tells the model who it is, how it should behave, and what the answer should look like. Think of it as the “job description” that stays the same no matter what question you ask later. It’s stable context.</li>
<li>The <strong>user prompt</strong> is the specific request you make right now, like checking the clarity of a particular title. It can change every time. The model responds to this request within the “job description”.</li>
</ul>
<p>If you put everything into the user prompt, you end up repeating rules and formatting instructions over and over, which makes the conversation messy and can confuse the model (aka “context pollution”). By keeping the stable rules and output format in the system prompt, and putting specifics or content in the user prompt, you get clearer, more reliable answers. In both <code>chatlas</code> and <code>ellmer</code> you can use the <code>system_prompt</code> argument when initialising the <code>chat</code> object.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Why put the details in a system prompt?
</div>
</div>
<div class="callout-body-container callout-body">
<p>Even though past messages are sent along in a conversation, putting the fixed details in the system prompt still makes a difference. The model pays more attention to instructions in the system prompt, so they’re less likely to be ignored or overridden. It also keeps the chat history cleaner, since you don’t need to repeat the same background and formatting rules in every user message. This way, the user prompt stays focused on just the part that changes (like a new title) while the system prompt handles the rest.</p>
</div>
</div>
<p>So what to do if you did all that and still get answers that are not up to your standards? In that case, you can try <strong>Chain-of-Thought prompting</strong>, where you ask the model to explain its reasoning step-by-step before answering. This makes complex tasks easier to follow and helps you understand how the model thinks. It’s great for debugging to see where the model started to get off.</p>
<p>Now, with all that knowledge, let’s change our prompt from:</p>
<div class="sourceCode" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb19-1">I'm working on a presentation with the title: 'The Shiny Side of LLMs'. What's your feedback just based on that title?</span></code></pre></div>
<p>To the following system prompt:</p>
<div class="sourceCode" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb20-1">You are a presentation coach for data scientists. </span>
<span id="cb20-2">You give constructive, focused, and practical feedback on titles, structure, and storytelling. </span>
<span id="cb20-3"></span>
<span id="cb20-4">The presentation you are helping with is a 10-minute lightning talk at posit::conf(2025).  </span>
<span id="cb20-5">The audience is Python and R users who are curious about AI and large language models, </span>
<span id="cb20-6">but not all of them have a deep technical background. </span>
<span id="cb20-7">The talk uses Shiny as a way to demo and explore LLMs in practice. </span>
<span id="cb20-8"></span>
<span id="cb20-9">Always return your feedback as a JSON array of objects, where each object has the following keys:</span>
<span id="cb20-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>'aspect': one of 'clarity', 'tone', or 'relevance'</span>
<span id="cb20-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>'feedback': a concise assessment</span>
<span id="cb20-12"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>'suggestion': an optional improvement if applicable</span></code></pre></div>
<p>And related user prompt for the task at hand:</p>
<div class="sourceCode" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb21-1"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Evaluate the title:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> 'The Shiny Side of LLMs'</span></span></code></pre></div>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-6-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-1" aria-controls="tabset-6-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-6-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-2" aria-controls="tabset-6-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-6-1" class="tab-pane active" aria-labelledby="tabset-6-1-tab">
<div class="sourceCode" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb22-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic</span>
<span id="cb22-2"></span>
<span id="cb22-3">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb22-4">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb22-5">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb22-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    You are a presentation coach for data scientists. </span></span>
<span id="cb22-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    You give constructive, focused, and practical feedback on titles, structure, and storytelling. </span></span>
<span id="cb22-8"></span>
<span id="cb22-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    The presentation you are helping with is a 10-minute lightning talk at posit::conf(2025).  </span></span>
<span id="cb22-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    The audience is Python and R users who are curious about AI and large language models, </span></span>
<span id="cb22-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    but not all of them have a deep technical background. </span></span>
<span id="cb22-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    The talk uses Shiny as a way to demo and explore LLMs in practice. </span></span>
<span id="cb22-13"></span>
<span id="cb22-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    Always return your feedback as a JSON array of objects, where each object has the following keys:</span></span>
<span id="cb22-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    - 'aspect': one of 'clarity', 'tone', or 'relevance'</span></span>
<span id="cb22-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    - 'feedback': a concise assessment</span></span>
<span id="cb22-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    - 'suggestion': an optional improvement if applicable</span></span>
<span id="cb22-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    """</span>,</span>
<span id="cb22-19">)</span>
<span id="cb22-20"></span>
<span id="cb22-21">chat.chat(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate the title: 'The Shiny Side of LLMs'"</span>)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb23-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  [                                                                                                                 </span></span>
<span id="cb23-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   {                                                                                                               </span></span>
<span id="cb23-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "aspect": "clarity",                                                                                          </span></span>
<span id="cb23-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "feedback": "The title uses a clever play on words with 'Shiny' but may create confusion about whether this i </span></span>
<span id="cb23-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> primarily about the Shiny framework or LLMs. The wordplay might obscure the main focus for attendees scanning the </span></span>
<span id="cb23-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> program.",                                                                                                        </span></span>
<span id="cb23-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "suggestion": "Consider a title that more clearly positions Shiny as the tool and LLMs as the subject, such a </span></span>
<span id="cb23-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> 'Exploring LLMs with Shiny: Interactive AI Demos Made Simple'"                                                    </span></span>
<span id="cb23-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                                              </span></span>
<span id="cb23-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   {                                                                                                               </span></span>
<span id="cb23-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "aspect": "tone",                                                                                             </span></span>
<span id="cb23-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "feedback": "The playful tone with 'Shiny Side' is appropriate for a lightning talk and fits the Posit        </span></span>
<span id="cb23-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> conference atmosphere. It suggests an accessible, positive approach that aligns well with the audience's curiosit </span></span>
<span id="cb23-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> about AI.",                                                                                                       </span></span>
<span id="cb23-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "suggestion": null                                                                                            </span></span>
<span id="cb23-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                                              </span></span>
<span id="cb23-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   {                                                                                                               </span></span>
<span id="cb23-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "aspect": "relevance",                                                                                        </span></span>
<span id="cb23-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "feedback": "Highly relevant for the audience - Python/R users at Posit::conf would immediately recognize     </span></span>
<span id="cb23-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> 'Shiny' and appreciate learning how to apply it to trending AI/LLM topics. The focus on practical demos matches   </span></span>
<span id="cb23-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> what this audience values.",                                                                                      </span></span>
<span id="cb23-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "suggestion": "Consider adding a subtitle to reinforce the practical angle: 'The Shiny Side of LLMs: Building </span></span>
<span id="cb23-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Interactive AI Demos'"                                                                                            </span></span>
<span id="cb23-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   }                                                                                                               </span></span>
<span id="cb23-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> ]</span></span>
<span id="cb23-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> """</span></span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-6-2" class="tab-pane" aria-labelledby="tabset-6-2-tab">
<div class="sourceCode" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb24-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb24-2"></span>
<span id="cb24-3">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb24-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb24-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb24-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  You are a presentation coach for data scientists. </span></span>
<span id="cb24-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  You give constructive, focused, and practical feedback on titles, structure, and storytelling. </span></span>
<span id="cb24-8"></span>
<span id="cb24-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  The presentation you are helping with is a 10-minute lightning talk at posit::conf(2025).  </span></span>
<span id="cb24-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  The audience is Python and R users who are curious about AI and large language models, </span></span>
<span id="cb24-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  but not all of them have a deep technical background. </span></span>
<span id="cb24-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  The talk uses Shiny as a way to demo and explore LLMs in practice. </span></span>
<span id="cb24-13"></span>
<span id="cb24-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  Always return your feedback as a JSON array of objects, where each object has the following keys:</span></span>
<span id="cb24-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  - 'aspect': one of 'clarity', 'tone', or 'relevance'</span></span>
<span id="cb24-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  - 'feedback': a concise assessment</span></span>
<span id="cb24-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  - 'suggestion': an optional improvement if applicable</span></span>
<span id="cb24-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  "</span></span>
<span id="cb24-19">)</span>
<span id="cb24-20"></span>
<span id="cb24-21">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate the title: 'The Shiny Side of LLMs'"</span>)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb25-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ```json</span></span>
<span id="cb25-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [</span></span>
<span id="cb25-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   {</span></span>
<span id="cb25-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "aspect": "clarity",</span></span>
<span id="cb25-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "feedback": "The title is somewhat ambiguous - 'shiny side' could refer to positive aspects of LLMs or literally to the Shiny framework. This wordplay might confuse audiences unfamiliar with both concepts.",</span></span>
<span id="cb25-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "suggestion": "Consider 'Exploring LLMs with Shiny: Interactive AI Demos' or 'Building LLM Interfaces with Shiny' for clearer technical focus"</span></span>
<span id="cb25-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb25-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   {</span></span>
<span id="cb25-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "aspect": "tone",</span></span>
<span id="cb25-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "feedback": "The playful wordplay fits well with the posit::conf audience and lightning talk format. It's approachable and not intimidating, which works for mixed technical backgrounds.",</span></span>
<span id="cb25-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "suggestion": null</span></span>
<span id="cb25-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb25-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   {</span></span>
<span id="cb25-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "aspect": "relevance",</span></span>
<span id="cb25-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "feedback": "Highly relevant for the audience - directly connects familiar Shiny technology with trending AI/LLM topics. Perfect for Python/R users wanting practical AI applications.",</span></span>
<span id="cb25-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "suggestion": null</span></span>
<span id="cb25-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   }</span></span>
<span id="cb25-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ]</span></span>
<span id="cb25-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ```</span></span></code></pre></div>
</details>
</div>
</div>
</div>
</div>
<p>That’s already something we can programmatically work with. Note that there are still differences between runs and between Python and R (just take a look at both examples). That is expected, even though our prompt is exactly the same. We’ll come back to that a bit later.</p>
</section>
<section id="choosing-the-right-input-format" class="level1">
<h1>Choosing the right input format</h1>
<p>The goal of the DeckCheck app is to do more than just looking at a title: we want to analyse Quarto slides. That means we need to provide our presentation in the chat. The question is… How do we do that? The most straightforward choice to provide this kind of data to an LLM is markdown. Markdown is lightweight, readable, and has a nice structure. It captures headings, bullet points, code blocks, and text, which is perfect.</p>
<p>But markdown isn’t the only option:</p>
<ul>
<li><strong>Plain text</strong> is the most basic. It’s simple, but you lose structure. Titles become regular text, as well as bullet points, code blocks etc. This makes it harder to analyse.</li>
<li><strong>HTML</strong> keeps all the structure but comes with a lot of noise (tags, attributes) that the LLM might happily read but we might not want to include.</li>
<li><strong>Structured JSON chunks</strong> give the most control, but is a big step from Quarto slides compared to plain markdown.</li>
</ul>
<p>If we’re starting from a Quarto <code>.qmd</code> or <code>.ipynb</code> file, we can:</p>
<ul>
<li>Directly render Quarto slides to markdown via <code>quarto render</code> with <code>-to markdown</code></li>
<li>We’re not getting into the nitty gritty of <a href="https://quarto.org/docs/authoring/markdown-basics.html">markdown vs Quarto</a>, but Quarto is basically markdown with some fancy extras, which can cause noise for our goal. So we might want to strip slide metadata, speaker notes, and other extras with custom filters or scripts, depending on what we want to keep (and to be honest: how sophisticated we want it to be).</li>
</ul>
<p>Let’s say we have a very basic Quarto presentation that talks about “The Shiny Side of LLMs”, loosely resembling what we’ve already talked about in this article, but is written in a sub-optimal way. The file is named <a href="https://github.com/hypebright/the-shiny-side-of-llms/blob/8857ca5ae4f74a86105ce9d1f04df3354eeee866/Quarto/my-presentation.qmd">“my-presentation.qmd”</a> and lives in a Quarto folder in our working directory.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
About the presentation file
</div>
</div>
<div class="callout-body-container callout-body">
<p>It’s not necessary to read this presentation line-by-line, it is just there for demo purposes and for reference if you want to make sense of the output that Claude is going to give later. In subsequent code we will simply refer to it as “my-presentation.qmd”.</p>
</div>
</div>
<div class="cell">
<details class="code-fold">
<summary>See full Quarto presentation</summary>
<div class="sourceCode cell-code" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb26-1"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb26-2">title<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The Shiny Side of LLMs"</span></span>
<span id="cb26-3">author<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Veerle Eeftink - van Leemput"</span></span>
<span id="cb26-4">format<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb26-5">  revealjs<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> </span>
<span id="cb26-6">    theme<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> [default]</span>
<span id="cb26-7">include<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>header<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> </span>
<span id="cb26-8">  text<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span></span>
<span id="cb26-9">    <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span>link href<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"</span> rel<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"stylesheet"</span> integrity<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"</span> crossorigin<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"anonymous"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb26-10">highlight<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>style<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"nord"</span></span>
<span id="cb26-11"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb26-12"></span>
<span id="cb26-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Hey 👋</span></span>
<span id="cb26-14"></span>
<span id="cb26-15">In this demo presentation we will explore how to use LLMs <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> Shiny applications. Because everybody is doing it, right? 😉</span>
<span id="cb26-16"></span>
<span id="cb26-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># What do you need?</span></span>
<span id="cb26-18"></span>
<span id="cb26-19"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> The means to authenticate. Depending on your provider, you might need an API <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">key</span> (e.g. Anthropic, OpenAI), a <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">token</span> (e.g. Hugging Face), or other authentication methods.</span>
<span id="cb26-20"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> Put those credentials <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> a suitable <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">place</span> (<span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> R<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.Renviron</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> Python<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.env</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>)</span>
<span id="cb26-21"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> Install the necessary packages<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb26-22">  <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> For Python<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chatlas</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shiny</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-23">  <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> For R<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ellmer</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shiny</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shinychat</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, and optionally <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bslib</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-24"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> An creative idea <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> your LLM<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>powered app<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span></span>
<span id="cb26-25"></span>
<span id="cb26-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># What are we building</span></span>
<span id="cb26-27"></span>
<span id="cb26-28">We<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'re building an app called DeckCheck: it'</span>s your favourite Presentation Rehearsal Buddy.</span>
<span id="cb26-29"></span>
<span id="cb26-30">With DeckCheck, you can upload your Quarto presentation and get tailored feedback<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span></span>
<span id="cb26-31"></span>
<span id="cb26-32">No more lengthy, boring presentations 🤠.</span>
<span id="cb26-33"></span>
<span id="cb26-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Where to start?</span></span>
<span id="cb26-35"></span>
<span id="cb26-36"><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.</span> Talk to an LLM with the help of <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chatlas</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> (Python) and <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ellmer</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> (R)</span>
<span id="cb26-37"><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.</span> Get a basic <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shiny</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> app ready</span>
<span id="cb26-38"><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.</span> Integrate <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chatlas</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ellmer</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> that <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shiny</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> app using a simple chat interface</span>
<span id="cb26-39"><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.</span> Build it out to an app where you can upload slides, see metrics about your presentation, and more.</span>
<span id="cb26-40"></span>
<span id="cb26-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Talk to an LLM with chatlas</span></span>
<span id="cb26-42"></span>
<span id="cb26-43">For Python, we can use <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chatlas</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb26-44"></span>
<span id="cb26-45">\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">python</span></span>
<span id="cb26-46"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">from chatlas import ChatAnthropic</span></span>
<span id="cb26-47"></span>
<span id="cb26-48"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chat = ChatAnthropic(</span></span>
<span id="cb26-49"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    model="claude-sonnet-4-20250514",</span></span>
<span id="cb26-50"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    system_prompt="You are a presentation coach for data scientists. You give constructive, focused, and practical feedback on titles, structure, and storytelling.",</span></span>
<span id="cb26-51"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb26-52"></span>
<span id="cb26-53"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chat.chat("I'm working on a presentation with the title: 'The Shiny Side of LLMs'. What's your feedback just based on that title?")</span></span>
<span id="cb26-54"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-55"></span>
<span id="cb26-56">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Talk to an LLM with ellmer</span></span>
<span id="cb26-57"></span>
<span id="cb26-58">For R, we can use <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ellmer</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb26-59"></span>
<span id="cb26-60">\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">r</span></span>
<span id="cb26-61"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">library(ellmer)</span></span>
<span id="cb26-62"></span>
<span id="cb26-63"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chat &lt;- chat_anthropic(</span></span>
<span id="cb26-64"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    model = "claude-sonnet-4-20250514",</span></span>
<span id="cb26-65"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    system_prompt = "You are a presentation coach for data scientists. You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span></span>
<span id="cb26-66"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb26-67"></span>
<span id="cb26-68"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chat$chat("I'm working on a presentation with the title: 'The Shiny Side of LLMs'. What's your feedback just based on that title?")</span></span>
<span id="cb26-69"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-70"></span>
<span id="cb26-71">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Why Shiny?</span></span>
<span id="cb26-72"></span>
<span id="cb26-73"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>   Shiny is a web application framework <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> R and Python that allows you to build interactive web applications.</span>
<span id="cb26-74"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>   It provides a reactive programming model, making it easy to create dynamic user interfaces and handle user inputs.</span>
<span id="cb26-75"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>   Perfect <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> your LLM<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>powered app<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span></span>
<span id="cb26-76"></span>
<span id="cb26-77">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny basics: Python</span></span>
<span id="cb26-78"></span>
<span id="cb26-79">\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">python</span></span>
<span id="cb26-80"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">from shiny import App, ui, render</span></span>
<span id="cb26-81"></span>
<span id="cb26-82"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"># Define UI</span></span>
<span id="cb26-83"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">app_ui = ui.page_fluid(</span></span>
<span id="cb26-84"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    ui.h1("DeckCheck"),</span></span>
<span id="cb26-85"></span>
<span id="cb26-86"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    # Card with content</span></span>
<span id="cb26-87"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    ui.card(</span></span>
<span id="cb26-88"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        ui.card_header("Welcome"),</span></span>
<span id="cb26-89"></span>
<span id="cb26-90"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        ui.p("This is a basic Shiny for Python application."),</span></span>
<span id="cb26-91"></span>
<span id="cb26-92"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        # Input: text</span></span>
<span id="cb26-93"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        ui.input_text_area("text", "What's your question?"),</span></span>
<span id="cb26-94"></span>
<span id="cb26-95"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        # Output: echo text back</span></span>
<span id="cb26-96"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        ui.output_text("echo")</span></span>
<span id="cb26-97"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    )</span></span>
<span id="cb26-98"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb26-99"></span>
<span id="cb26-100"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"># Define server</span></span>
<span id="cb26-101"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">def server(input, output, session):</span></span>
<span id="cb26-102"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    @render.text</span></span>
<span id="cb26-103"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    def echo():</span></span>
<span id="cb26-104"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        return f"You asked: {input.text()}"</span></span>
<span id="cb26-105"></span>
<span id="cb26-106"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"># Create app</span></span>
<span id="cb26-107"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">app = App(app_ui, server)</span></span>
<span id="cb26-108"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-109"></span>
<span id="cb26-110">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny basics: R</span></span>
<span id="cb26-111"></span>
<span id="cb26-112">Tip<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> combine with <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bslib</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> fancy looks<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span></span>
<span id="cb26-113"></span>
<span id="cb26-114">\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">r</span></span>
<span id="cb26-115"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">library(shiny)</span></span>
<span id="cb26-116"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">library(bslib)</span></span>
<span id="cb26-117"></span>
<span id="cb26-118"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ui &lt;- page_fluid(</span></span>
<span id="cb26-119"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  theme = bs_theme(bootswatch = "minty"),</span></span>
<span id="cb26-120"></span>
<span id="cb26-121"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  # App title</span></span>
<span id="cb26-122"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  h1("DeckCheck"),</span></span>
<span id="cb26-123"></span>
<span id="cb26-124"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  # Create a card</span></span>
<span id="cb26-125"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  card(</span></span>
<span id="cb26-126"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    card_header("Welcome"),</span></span>
<span id="cb26-127"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    p("This is a simple Shiny app using bslib for layout and styling."),</span></span>
<span id="cb26-128"></span>
<span id="cb26-129"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    # Input: text</span></span>
<span id="cb26-130"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    textAreaInput("text", "What's your question?"),</span></span>
<span id="cb26-131"></span>
<span id="cb26-132"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    # Output: echo the text back</span></span>
<span id="cb26-133"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    textOutput("echo")</span></span>
<span id="cb26-134"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  )</span></span>
<span id="cb26-135"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb26-136"></span>
<span id="cb26-137"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">server &lt;- function(input, output, session) {</span></span>
<span id="cb26-138"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  # Reactive output</span></span>
<span id="cb26-139"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  output$echo &lt;- renderText({</span></span>
<span id="cb26-140"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    paste("You asked:", input$text)</span></span>
<span id="cb26-141"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  })</span></span>
<span id="cb26-142"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb26-143"></span>
<span id="cb26-144"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shinyApp(ui, server)</span></span>
<span id="cb26-145"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-146"></span>
<span id="cb26-147">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Where's the magic?</span></span>
<span id="cb26-148"></span>
<span id="cb26-149">We<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'re here for some AI magic, right? So let'</span>s talk to an LLM via our Shiny application<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span></span>
<span id="cb26-150"></span>
<span id="cb26-151"><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>[AI Magic](https<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">//</span>media4.giphy.com<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>media<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>v1.Y2lkPTc5MGI3NjExMmZ0cmd1aGZzM3JqdW1zbng0czU2c2RwejNtM25jMW8xdnYzNXVrdCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>OlxeZT285uhFydNetO<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>giphy.gif)</span>
<span id="cb26-152"></span>
<span id="cb26-153">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny and chatlas</span></span>
<span id="cb26-154"></span>
<span id="cb26-155">We can add a chat component to our UI with <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ui.chat_ui</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> from <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shiny</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>, and use <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ChatAnthropic</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> from <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chatlas</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span> to send our messages to Claude. With minimal change, our Shiny <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> Python app is already LLM<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>powered<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span></span>
<span id="cb26-156"></span>
<span id="cb26-157"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">python</span></span>
<span id="cb26-158"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">from shiny import App, ui</span></span>
<span id="cb26-159"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">from chatlas import ChatAnthropic</span></span>
<span id="cb26-160"></span>
<span id="cb26-161"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"># Define UI</span></span>
<span id="cb26-162"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">app_ui = ui.page_fluid(</span></span>
<span id="cb26-163"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    ui.h1("DeckCheck"),</span></span>
<span id="cb26-164"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    # Card with chat component</span></span>
<span id="cb26-165"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    ui.card(</span></span>
<span id="cb26-166"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        ui.card_header("Get started"),</span></span>
<span id="cb26-167"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        ui.p("Ask me anything about your presentation 💡"),</span></span>
<span id="cb26-168"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        # Chat component</span></span>
<span id="cb26-169"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        ui.chat_ui(id="my_chat"),</span></span>
<span id="cb26-170"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    ),</span></span>
<span id="cb26-171"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb26-172"></span>
<span id="cb26-173"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"># Define server</span></span>
<span id="cb26-174"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">def server(input, output, session):</span></span>
<span id="cb26-175"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    chat_component = ui.Chat(id="my_chat")</span></span>
<span id="cb26-176"></span>
<span id="cb26-177"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    chat = ChatAnthropic(</span></span>
<span id="cb26-178"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        model="claude-sonnet-4-20250514",</span></span>
<span id="cb26-179"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        system_prompt="You are a presentation coach for data scientists. You give constructive, focused, and practical feedback on titles, structure, and storytelling.",</span></span>
<span id="cb26-180"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    )</span></span>
<span id="cb26-181"></span>
<span id="cb26-182"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    @chat_component.on_user_submit</span></span>
<span id="cb26-183"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    async def handle_user_input(user_input: str):</span></span>
<span id="cb26-184"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        response = await chat.stream_async(user_input)</span></span>
<span id="cb26-185"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        await chat_component.append_message_stream(response)</span></span>
<span id="cb26-186"></span>
<span id="cb26-187"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"># Create app</span></span>
<span id="cb26-188"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">app = App(app_ui, server)</span></span>
<span id="cb26-189"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-190"></span>
<span id="cb26-191">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Shiny, shinychat and ellmer We can add a chat component to our UI with `chat_mod_ui` from `shinychat`, and use `chat_anthropic` from `ellmer` to send our messages to Claude. With minimal change, our Shiny for R app is already LLM-powered!</span></span>
<span id="cb26-192"></span>
<span id="cb26-193">\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">r</span></span>
<span id="cb26-194"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">library(shiny)</span></span>
<span id="cb26-195"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">library(bslib)</span></span>
<span id="cb26-196"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">library(ellmer)</span></span>
<span id="cb26-197"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">library(shinychat)</span></span>
<span id="cb26-198"></span>
<span id="cb26-199"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ui &lt;- page_fluid(</span></span>
<span id="cb26-200"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  theme = bs_theme(bootswatch = "minty"),</span></span>
<span id="cb26-201"></span>
<span id="cb26-202"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  # App title</span></span>
<span id="cb26-203"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  h1("DeckCheck"),</span></span>
<span id="cb26-204"></span>
<span id="cb26-205"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  # Create a card</span></span>
<span id="cb26-206"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  card(</span></span>
<span id="cb26-207"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    card_header("Get started"),</span></span>
<span id="cb26-208"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    p("Ask me anything about your presentation 💡"),</span></span>
<span id="cb26-209"></span>
<span id="cb26-210"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    # Chat component</span></span>
<span id="cb26-211"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    chat_mod_ui("chat_component")</span></span>
<span id="cb26-212"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  )</span></span>
<span id="cb26-213"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb26-214"></span>
<span id="cb26-215"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">server &lt;- function(input, output, session) {</span></span>
<span id="cb26-216"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  chat &lt;- chat_anthropic(</span></span>
<span id="cb26-217"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    model = "claude-sonnet-4-20250514",</span></span>
<span id="cb26-218"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    system_prompt = "You are a presentation coach for data scientists. </span></span>
<span id="cb26-219"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span></span>
<span id="cb26-220"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  )</span></span>
<span id="cb26-221"></span>
<span id="cb26-222"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  chat_mod_server("chat_component", chat)</span></span>
<span id="cb26-223"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb26-224"></span>
<span id="cb26-225"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shinyApp(ui, server)</span></span>
<span id="cb26-226"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span>\<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">`</span></span>
<span id="cb26-227"></span>
<span id="cb26-228">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># User experience</span></span>
<span id="cb26-229"></span>
<span id="cb26-230">When using an LLM <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> a Shiny application, it is important to consider the user experience. The LLM should enhance the application, not overwhelm it. This means that the LLM<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'s responses should be concise and relevant to the user'</span>s query. Additionally, it is crucial to handle errors gracefully, providing users with helpful feedback <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> the LLM cannot generate a response or <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> there are issues with the API. Therefore, implementing proper error handling and fallback mechanisms is essential.</span>
<span id="cb26-231"></span>
<span id="cb26-232">\<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Up next</span></span>
<span id="cb26-233"></span>
<span id="cb26-234">Of course we<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'re not ready yet. Next steps include:</span></span>
<span id="cb26-235"></span>
<span id="cb26-236"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">-   Adding a file upload button</span></span>
<span id="cb26-237"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">## Reading the content of the presentation</span></span>
<span id="cb26-238"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">-   Crafting our prompt</span></span>
<span id="cb26-239"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">-   Extract information in a structured way</span></span>
<span id="cb26-240"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">-   Display that information in a visually appealing manner (value boxes, use of colours, icons, functionality to export the feedback...</span></span>
<span id="cb26-241"></span>
<span id="cb26-242"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">\# Thank you!</span></span></code></pre></div>
</details>
</div>
<p>This presentation would be suitable for a lightning talk (a maximum of 10 minutes), and for an audience that is not yet familiar with <code>shiny</code> and <code>chatlas</code>/<code>ellmer</code>.</p>
<p>To turn this <code>.qmd</code> file programmatically into markdown (<code>.md</code>) we would need to call quarto from our system. This can be done via the CLI or, in the case of R, via the <a href="https://quarto-dev.github.io/quarto-r/"><code>quarto</code> package</a>. Note that our simple presentation doesn’t make much use of the fancy stuff you can do with Quarto, so our <code>.md</code> file will almost look exactly the same as our <code>.qmd</code> file. But keep in mind we’re building for a wider audience!</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-7-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-1" aria-controls="tabset-7-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-7-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-2" aria-controls="tabset-7-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-7-1" class="tab-pane active" aria-labelledby="tabset-7-1-tab">
<p>We can run quarto from the CLI with <code>subprocess</code>:</p>
<div class="sourceCode" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb27-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> subprocess</span>
<span id="cb27-2"></span>
<span id="cb27-3">subprocess.run([<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>])</span></code></pre></div>
</div>
<div id="tabset-7-2" class="tab-pane" aria-labelledby="tabset-7-2-tab">
<p>Using the <code>quarto</code> package:</p>
<div class="sourceCode" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb28-1">quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-presentation.qmd"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>)</span></code></pre></div>
<p>or as a sytem command:</p>
<div class="sourceCode" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb29-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">system</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto render my-presentation.qmd --to markdown"</span>)</span></code></pre></div>
</div>
</div>
</div>
<p>Now our system prompt can look something like this:</p>
<div class="sourceCode" id="cb30" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb30-1">You are a presentation coach for data scientists that analyses presentation slide decks written in Markdown. </span>
<span id="cb30-2">You extract key information, evaluate quality, and return structured feedback that is constructive, focused and practical.</span>
<span id="cb30-3"></span>
<span id="cb30-4">The presentation you are helping with is a 10-minute lightning talk at posit::conf(2025).  </span>
<span id="cb30-5">The audience is Python and R users who are curious about AI and large language models, </span>
<span id="cb30-6">but not all of them have a deep technical background. </span>
<span id="cb30-7">The talk uses Shiny as a way to demo and explore LLMs in practice. </span>
<span id="cb30-8"></span>
<span id="cb30-9">You extract the following information:</span>
<span id="cb30-10"></span>
<span id="cb30-11">...</span>
<span id="cb30-12"></span>
<span id="cb30-13">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span>
<span id="cb30-14"></span>
<span id="cb30-15">...</span>
<span id="cb30-16"></span>
<span id="cb30-17">Always return your answer as a JSON object with the following structure:</span>
<span id="cb30-18"></span>
<span id="cb30-19">...</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show full prompt</summary>
<div class="sourceCode cell-code" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb31-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb31-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You are a presentation coach for data scientists that analyses presentation slide decks written in Markdown. </span></span>
<span id="cb31-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract key information, evaluate quality, and return structured feedback that is constructive, focused and practical.</span></span>
<span id="cb31-4"></span>
<span id="cb31-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The presentation you are helping with is a 10-minute lightning talk at posit::conf(2025).  </span></span>
<span id="cb31-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The audience is Python and R users who are curious about AI and large language models, </span></span>
<span id="cb31-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">but not all of them have a deep technical background. </span></span>
<span id="cb31-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The talk uses Shiny as a way to demo and explore LLMs in practice. </span></span>
<span id="cb31-9"></span>
<span id="cb31-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb31-11"></span>
<span id="cb31-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. The presentation title</span></span>
<span id="cb31-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Total number of slides</span></span>
<span id="cb31-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Percentage of slides containing code blocks</span></span>
<span id="cb31-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Percentage of slides containing images</span></span>
<span id="cb31-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Estimated presentation length (in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide)</span></span>
<span id="cb31-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Tone (a brief description)</span></span>
<span id="cb31-18"></span>
<span id="cb31-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span></span>
<span id="cb31-20"></span>
<span id="cb31-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. Clarity of content: evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?</span></span>
<span id="cb31-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Relevance for intended audience: assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?</span></span>
<span id="cb31-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Visual design: judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?</span></span>
<span id="cb31-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Engagement: estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?</span></span>
<span id="cb31-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Pacing: analyze the distribution of content across slides. Are some slides too dense or too light? </span></span>
<span id="cb31-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Structure: review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?</span></span>
<span id="cb31-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">7. consistency: evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?</span></span>
<span id="cb31-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">8. Accessibility: consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?</span></span>
<span id="cb31-29"></span>
<span id="cb31-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Always return your answer as a JSON object with the following structure:</span></span>
<span id="cb31-31"></span>
<span id="cb31-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb31-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "presentation_title": "",</span></span>
<span id="cb31-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "total_slides": 0,</span></span>
<span id="cb31-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "percent_with_code": 0,</span></span>
<span id="cb31-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "percent_with_images": 0,</span></span>
<span id="cb31-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "estimated_duration_minutes": 0,</span></span>
<span id="cb31-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "tone": "",</span></span>
<span id="cb31-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "clarity": {</span></span>
<span id="cb31-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb31-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "relevance": {</span></span>
<span id="cb31-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb31-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "visual_design": {</span></span>
<span id="cb31-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb31-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "engagement": {</span></span>
<span id="cb31-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb31-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "pacing": {</span></span>
<span id="cb31-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb31-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "structure": {</span></span>
<span id="cb31-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb31-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "concistency": {</span></span>
<span id="cb31-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-66"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb31-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "accessibility": {</span></span>
<span id="cb31-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb31-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb31-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  }</span></span>
<span id="cb31-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb31-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
<p>And our user prompt:</p>
<div class="sourceCode" id="cb32" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb32-1"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Here are the slides in Markdown:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> ...</span></span></code></pre></div>
</section>
<section id="storing-prompts-and-dynamic-data" class="level1">
<h1>Storing prompts and dynamic data</h1>
<p>As you know by know, you need to provide very specific instructions in your prompt if you’re looking for good outcomes. And the more you specify, the lengthier your prompt becomes. Eventually this is going to be very annoying to put in your code directly. Therefore, it’s a good idea to store your prompt somewhere. We determined markdown is the way to go for inputting slide content, but the same motivation applies to our prompt. Besides that, markdown allows us to structure our prompt more clearly too: headers, bullet points, numbered lists… That’s nice for the LLM and for you!</p>
<p>The recommended file naming convention is to use <code>prompt.md</code> if you only have one prompt, and use informative names like <code>prompt-analyse-slides.md</code> if you have multiple. For clarity, we’ll go with the latter even though we only have one prompt (so far).</p>
<p>Since your prompt(s) are very important for the outcome of your LLM-powered app, it’s also a good idea to track these files using git. You don’t want to lose that precious carefully prompt that worked so well before you decided to change the whole thing.</p>
<p>Prompt(s) don’t have to be static: they can be dynamic too. We want our app to improve Quarto presentations world-wide: many users, many Quarto slides, many audiences and presentation lengths. Our prompt needs to be able to work with this dynamic data, so our prompt needs to change depending on those variables. Luckily, we can use the <code>interpolate_file()</code> or <code>interpolate()</code> function, which, as the names suggest, can interpolate variables into a prompt template stored in a file, or in a simple string. To do so, we use the <code>{ x }</code> syntax, making our <code>prompt-analyse-slides.md</code> file look like this:</p>
<div class="sourceCode" id="cb33" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb33-1">...</span>
<span id="cb33-2"></span>
<span id="cb33-3">The presentation you are helping with is a {{ length }}-minute {{ type }} at {{ event }}.  </span>
<span id="cb33-4">The audience is {{ audience }}. </span>
<span id="cb33-5"></span>
<span id="cb33-6">...</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show full prompt</summary>
<div class="sourceCode cell-code" id="cb34" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb34-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb34-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You are a presentation coach for data scientists that analyses presentation slide decks written in Markdown. </span></span>
<span id="cb34-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract key information, evaluate quality, and return structured feedback that is constructive, focused and practical.</span></span>
<span id="cb34-4"></span>
<span id="cb34-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The presentation you are helping with is a {{ length }}-minute {{ type }} at {{ event }}.  </span></span>
<span id="cb34-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The audience is {{ audience }}. </span></span>
<span id="cb34-7"></span>
<span id="cb34-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb34-9"></span>
<span id="cb34-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. The presentation title</span></span>
<span id="cb34-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Total number of slides</span></span>
<span id="cb34-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Percentage of slides containing code blocks</span></span>
<span id="cb34-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Percentage of slides containing images</span></span>
<span id="cb34-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Estimated presentation length (in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide)</span></span>
<span id="cb34-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Tone (a brief description)</span></span>
<span id="cb34-16"></span>
<span id="cb34-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span></span>
<span id="cb34-18"></span>
<span id="cb34-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. Clarity of content: evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?</span></span>
<span id="cb34-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Relevance for intended audience: assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?</span></span>
<span id="cb34-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Visual design: judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?</span></span>
<span id="cb34-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Engagement: estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?</span></span>
<span id="cb34-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Pacing: analyze the distribution of content across slides. Are some slides too dense or too light? </span></span>
<span id="cb34-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Structure: review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?</span></span>
<span id="cb34-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">7. consistency: evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?</span></span>
<span id="cb34-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">8. Accessibility: consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?</span></span>
<span id="cb34-27"></span>
<span id="cb34-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Always return your answer as a JSON object with the following structure:</span></span>
<span id="cb34-29"></span>
<span id="cb34-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb34-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "presentation_title": "",</span></span>
<span id="cb34-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "total_slides": 0,</span></span>
<span id="cb34-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "percent_with_code": 0,</span></span>
<span id="cb34-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "percent_with_images": 0,</span></span>
<span id="cb34-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "estimated_duration_minutes": 0,</span></span>
<span id="cb34-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "tone": "",</span></span>
<span id="cb34-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "clarity": {</span></span>
<span id="cb34-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb34-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "relevance": {</span></span>
<span id="cb34-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb34-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "visual_design": {</span></span>
<span id="cb34-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb34-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "engagement": {</span></span>
<span id="cb34-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb34-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "pacing": {</span></span>
<span id="cb34-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb34-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "structure": {</span></span>
<span id="cb34-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb34-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "concistency": {</span></span>
<span id="cb34-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb34-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "accessibility": {</span></span>
<span id="cb34-66"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb34-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": ""</span></span>
<span id="cb34-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  }</span></span>
<span id="cb34-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb34-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
<p>Our user prompt is going directly into the <code>chat</code> method and will be:</p>
<div class="sourceCode" id="cb35" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb35-1"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Here are the slides in Markdown:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> {{ markdown }}</span></span></code></pre></div>
<p>Note: if your user prompt is long you might want to put this in a separate prompt file as well.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>You might wonder why we don’t we put the Markdown content in our system prompt. The reason is that we don’t want to overload our system prompt with big chunks of data like our Markdown slides. As mentioned previously, the system prompt sets the role, the rules, and provides task instructions. It is stable context. The Markdown slides are the actual content to analyse, and they change from one run to the other. This fits better in a user prompt. It’s also good practice to keep the system prompt clean so it is reusable across runs.</p>
</div>
</div>
<p>Those are prompts we can work with! So let’s chat away:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-8-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-1" aria-controls="tabset-8-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-8-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-2" aria-controls="tabset-8-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-8-1" class="tab-pane active" aria-labelledby="tabset-8-1-tab">
<div class="sourceCode" id="cb36" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb36-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic, interpolate_file, interpolate</span>
<span id="cb36-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> subprocess</span>
<span id="cb36-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb36-4"></span>
<span id="cb36-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown</span></span>
<span id="cb36-6">subprocess.run(</span>
<span id="cb36-7">    [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>]</span>
<span id="cb36-8">)</span>
<span id="cb36-9"></span>
<span id="cb36-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb36-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb36-12">audience_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb36-13">length_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb36-14">type_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb36-15">event_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb36-16"></span>
<span id="cb36-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb36-18">markdown_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span>)</span>
<span id="cb36-19">markdown_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> markdown_file.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb36-20"></span>
<span id="cb36-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb36-22">system_prompt_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides.md"</span>)</span>
<span id="cb36-23"></span>
<span id="cb36-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb36-25">system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb36-26">    system_prompt_file,</span>
<span id="cb36-27">    variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb36-28">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: audience_content,</span>
<span id="cb36-29">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: length_content,</span>
<span id="cb36-30">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: type_content,</span>
<span id="cb36-31">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: event_content,</span>
<span id="cb36-32">    },</span>
<span id="cb36-33">)</span>
<span id="cb36-34"></span>
<span id="cb36-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb36-36">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb36-37">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb36-38">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb36-39">)</span>
<span id="cb36-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb36-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb36-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb36-43">chat.chat(interpolate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>))</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb37" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb37-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb37-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  {                                                                                  </span></span>
<span id="cb37-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "presentation_title": "The Shiny Side of LLMs",                                  </span></span>
<span id="cb37-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "total_slides": 16,                                                              </span></span>
<span id="cb37-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "percent_with_code": 44,                                                         </span></span>
<span id="cb37-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "percent_with_images": 6,                                                        </span></span>
<span id="cb37-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "estimated_duration_minutes": 22,                                                </span></span>
<span id="cb37-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "tone": "Casual, enthusiastic, and approachable with emojis and informal languag </span></span>
<span id="cb37-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> aimed at making technical concepts accessible",                                    </span></span>
<span id="cb37-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "clarity": {                                                                     </span></span>
<span id="cb37-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 7,                                                                    </span></span>
<span id="cb37-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Content is generally clear with good step-by-step progressio </span></span>
<span id="cb37-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> but some technical concepts could benefit from more explanation for non-technical  </span></span>
<span id="cb37-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> audience members. Code examples are well-structured but may be overwhelming for    </span></span>
<span id="cb37-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> beginners."                                                                        </span></span>
<span id="cb37-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                               </span></span>
<span id="cb37-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "relevance": {                                                                   </span></span>
<span id="cb37-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 8,                                                                    </span></span>
<span id="cb37-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Well-targeted for the audience - provides both Python and R  </span></span>
<span id="cb37-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> examples, acknowledges varying technical backgrounds, and focuses on practical     </span></span>
<span id="cb37-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> implementation rather than deep theory. The DeckCheck example is relatable to the  </span></span>
<span id="cb37-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> conference audience."                                                              </span></span>
<span id="cb37-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                               </span></span>
<span id="cb37-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "visual_design": {                                                               </span></span>
<span id="cb37-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 5,                                                                    </span></span>
<span id="cb37-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Heavy reliance on text and code blocks with minimal visual   </span></span>
<span id="cb37-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> elements. Only one image (GIF) used. Code blocks dominate several slides, making   </span></span>
<span id="cb37-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> them potentially overwhelming. Layout appears basic without clear visual hierarchy </span></span>
<span id="cb37-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                               </span></span>
<span id="cb37-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "engagement": {                                                                  </span></span>
<span id="cb37-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 6,                                                                    </span></span>
<span id="cb37-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Uses emojis and casual language to maintain interest, includ </span></span>
<span id="cb37-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> a relatable example (DeckCheck), and has one engaging GIF. However, lacks          </span></span>
<span id="cb37-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> interactive elements beyond code examples and could benefit from more storytelling </span></span>
<span id="cb37-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> elements."                                                                         </span></span>
<span id="cb37-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                               </span></span>
<span id="cb37-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "pacing": {                                                                      </span></span>
<span id="cb37-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 4,                                                                    </span></span>
<span id="cb37-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Uneven pacing with several code-heavy slides that would      </span></span>
<span id="cb37-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> require significant time to explain, while some slides have minimal content. The   </span></span>
<span id="cb37-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> estimated 22-minute duration exceeds the 10-minute lightning talk format           </span></span>
<span id="cb37-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> considerably."                                                                     </span></span>
<span id="cb37-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                               </span></span>
<span id="cb37-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "structure": {                                                                   </span></span>
<span id="cb37-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 8,                                                                    </span></span>
<span id="cb37-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Clear logical progression from introduction to requirements, </span></span>
<span id="cb37-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> basic concepts, implementation examples, and next steps. Good parallel structure   </span></span>
<span id="cb37-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> showing both Python and R approaches. Solid beginning, middle, and end."           </span></span>
<span id="cb37-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                               </span></span>
<span id="cb37-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "concistency": {                                                                 </span></span>
<span id="cb37-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 7,                                                                    </span></span>
<span id="cb37-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Maintains consistent casual tone and formatting throughout.  </span></span>
<span id="cb37-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Code examples follow similar patterns for both languages. Some inconsistency in    </span></span>
<span id="cb37-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> slide density and the single image feels somewhat disconnected from the overall    </span></span>
<span id="cb37-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> style."                                                                            </span></span>
<span id="cb37-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                               </span></span>
<span id="cb37-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "accessibility": {                                                               </span></span>
<span id="cb37-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 6,                                                                    </span></span>
<span id="cb37-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Code blocks may be challenging for those with visual         </span></span>
<span id="cb37-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> impairments due to syntax highlighting and smaller fonts. The casual tone and emoj </span></span>
<span id="cb37-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> help with cognitive accessibility, but the technical density could be overwhelming </span></span>
<span id="cb37-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> for some audience members."                                                        </span></span>
<span id="cb37-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   }                                                                                </span></span>
<span id="cb37-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb37-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-8-2" class="tab-pane" aria-labelledby="tabset-8-2-tab">
<div class="sourceCode" id="cb38" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb38-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb38-2"></span>
<span id="cb38-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown</span></span>
<span id="cb38-4">quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb38-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>,</span>
<span id="cb38-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span></span>
<span id="cb38-7">)</span>
<span id="cb38-8"></span>
<span id="cb38-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb38-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb38-11">audience_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb38-12">length_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb38-13">type_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb38-14">event_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb38-15"></span>
<span id="cb38-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb38-17">markdown_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span></span>
<span id="cb38-18">markdown_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(markdown_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(markdown_file))</span>
<span id="cb38-19"></span>
<span id="cb38-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb38-21">system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides.md"</span></span>
<span id="cb38-22"></span>
<span id="cb38-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb38-24">system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb38-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb38-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> audience_content,</span>
<span id="cb38-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> length_content,</span>
<span id="cb38-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_content,</span>
<span id="cb38-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> event_content</span>
<span id="cb38-30">)</span>
<span id="cb38-31"></span>
<span id="cb38-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb38-33">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb38-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb38-35">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt,</span>
<span id="cb38-36">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span>(</span>
<span id="cb38-37">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temperature =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb38-38">  )</span>
<span id="cb38-39">)</span>
<span id="cb38-40"></span>
<span id="cb38-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb38-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb38-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb38-44">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb38-45">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb38-46">))</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb39" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb39-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; {</span></span>
<span id="cb39-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "presentation_title": "The Shiny Side of LLMs",</span></span>
<span id="cb39-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "total_slides": 16,</span></span>
<span id="cb39-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percent_with_code": 37.5,</span></span>
<span id="cb39-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percent_with_images": 6.25,</span></span>
<span id="cb39-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "estimated_duration_minutes": 14,</span></span>
<span id="cb39-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "tone": "Casual, friendly, and enthusiastic with emoji use and conversational </span></span>
<span id="cb39-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; language that makes technical content approachable",</span></span>
<span id="cb39-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "clarity": {</span></span>
<span id="cb39-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 7,</span></span>
<span id="cb39-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Content is generally clear with good progression from basics </span></span>
<span id="cb39-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; to implementation. However, some technical details could benefit from more </span></span>
<span id="cb39-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; explanation, and the system prompts in code examples could be better integrated </span></span>
<span id="cb39-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; into the narrative."</span></span>
<span id="cb39-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb39-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "relevance": {</span></span>
<span id="cb39-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 8,</span></span>
<span id="cb39-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Excellent match for the audience - shows practical </span></span>
<span id="cb39-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; integration of LLMs with familiar tools (Shiny), provides both Python and R </span></span>
<span id="cb39-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; examples, and focuses on a relatable use case (presentation feedback) that </span></span>
<span id="cb39-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; resonates with the posit::conf audience."</span></span>
<span id="cb39-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb39-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "visual_design": {</span></span>
<span id="cb39-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 6,</span></span>
<span id="cb39-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Basic markdown structure is clean but lacks visual hierarchy </span></span>
<span id="cb39-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; and design elements. Code blocks are well-formatted, but slides could benefit from </span></span>
<span id="cb39-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; more visual breaks, bullet points, and consistent formatting. The single GIF adds </span></span>
<span id="cb39-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; some visual interest."</span></span>
<span id="cb39-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb39-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "engagement": {</span></span>
<span id="cb39-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 7,</span></span>
<span id="cb39-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Good use of humor, emojis, and relatable scenarios (boring </span></span>
<span id="cb39-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; presentations). The progression from simple to complex keeps interest, and the </span></span>
<span id="cb39-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; practical demo concept is engaging. Could benefit from more interactive elements or</span></span>
<span id="cb39-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; storytelling."</span></span>
<span id="cb39-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb39-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "pacing": {</span></span>
<span id="cb39-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 6,</span></span>
<span id="cb39-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Some slides are too dense (UX slide, code-heavy slides) while</span></span>
<span id="cb39-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; others are very light (Thank you slide). The progression is logical but could be </span></span>
<span id="cb39-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; more evenly distributed. Code slides may take longer than estimated."</span></span>
<span id="cb39-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb39-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "structure": {</span></span>
<span id="cb39-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 8,</span></span>
<span id="cb39-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Clear logical flow from introduction to requirements, basic </span></span>
<span id="cb39-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; concepts, implementation, and next steps. Good parallel structure showing both </span></span>
<span id="cb39-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Python and R implementations. Strong opening and clear progression toward building </span></span>
<span id="cb39-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; the demo app."</span></span>
<span id="cb39-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb39-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "concistency": {</span></span>
<span id="cb39-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 7,</span></span>
<span id="cb39-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Generally consistent tone and approach throughout. Code </span></span>
<span id="cb39-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; formatting is consistent between languages. However, some slides vary significantly</span></span>
<span id="cb39-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; in content density, and the casual tone occasionally conflicts with more technical </span></span>
<span id="cb39-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; sections."</span></span>
<span id="cb39-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb39-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "accessibility": {</span></span>
<span id="cb39-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 5,</span></span>
<span id="cb39-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Basic markdown structure is accessible, but lacks </span></span>
<span id="cb39-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; considerations for screen readers (no alt text for the GIF), may have contrast </span></span>
<span id="cb39-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; issues depending on theme, and code blocks could be challenging for some users. </span></span>
<span id="cb39-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Font sizes and spacing depend on presentation software."</span></span>
<span id="cb39-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   }</span></span>
<span id="cb39-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; }</span></span></code></pre></div>
</details>
</div>
</div>
</div>
</div>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Inconsistencies in the output
</div>
</div>
<div class="callout-body-container callout-body">
<p>Did you already notice that some meta-data, like the percentage of slides with code and/or images is inconsistent? For example, the “percent_with_code” is 40, but other times it’s 37.5 or 45. It seems like a broken calculator! To understand why this happens you can read <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/">part 1</a> again, where we talk about why LLMs are really good at some tasks, and others, well… Not so much. That doesn’t mean it’s completely out of our hands. We can help the LLM with a “tool”. Keep on reading to learn more about that concept.</p>
</div>
</div>
<p>It’s not too bad for a first try and it serves as a basis we can depart from. We can go back and forth many times, aka do some “prompt engineering”, to make sure we are getting results that are usable. The prompt that we used mostly returns a broad analysis (makes sense, we asked it to), but if we want our users to take action we need to provide specific improvements. So we could extend our prompt with “provide specific and actionable improvements” and provide specific instructions like “keep each suggestion concise and mention the slide number(s) if applicable”:</p>
<div class="sourceCode" id="cb40" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb40-1">...</span>
<span id="cb40-2"></span>
<span id="cb40-3">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span>
<span id="cb40-4"></span>
<span id="cb40-5">...</span>
<span id="cb40-6"></span>
<span id="cb40-7">For each of the above scoring categories, provide specific and actionable improvements. Follow these instructions:</span>
<span id="cb40-8"></span>
<span id="cb40-9"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Keep each suggestion concise and mention the slide number(s) if applicable.</span>
<span id="cb40-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Do not invent issues and only suggest improvements when the content would clearly benefit from them.</span>
<span id="cb40-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>For each catogory, estimate what the new score would be if these improvements are implemented.</span>
<span id="cb40-12"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Return the improvement and new score as part of the response for that category.</span>
<span id="cb40-13"></span>
<span id="cb40-14">...</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show full prompt</summary>
<div class="sourceCode cell-code" id="cb41" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb41-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb41-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You are a presentation coach for data scientists that analyses presentation slide decks written in Markdown. </span></span>
<span id="cb41-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract key information, evaluate quality, and return structured feedback that is constructive, focused and practical.</span></span>
<span id="cb41-4"></span>
<span id="cb41-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The presentation you are helping with is a {{ length }}-minute {{ type }} at {{ event }}.  </span></span>
<span id="cb41-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The audience is {{ audience }}. </span></span>
<span id="cb41-7"></span>
<span id="cb41-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb41-9"></span>
<span id="cb41-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. The presentation title</span></span>
<span id="cb41-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Total number of slides</span></span>
<span id="cb41-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Percentage of slides containing code blocks</span></span>
<span id="cb41-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Percentage of slides containing images</span></span>
<span id="cb41-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Estimated presentation length (in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide)</span></span>
<span id="cb41-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Tone (a brief description)</span></span>
<span id="cb41-16"></span>
<span id="cb41-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span></span>
<span id="cb41-18"></span>
<span id="cb41-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. Clarity of content: evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?</span></span>
<span id="cb41-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Relevance for intended audience: assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?</span></span>
<span id="cb41-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Visual design: judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?</span></span>
<span id="cb41-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Engagement: estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?</span></span>
<span id="cb41-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Pacing: analyze the distribution of content across slides. Are some slides too dense or too light? </span></span>
<span id="cb41-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Structure: review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?</span></span>
<span id="cb41-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">7. consistency: evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?</span></span>
<span id="cb41-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">8. Accessibility: consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?</span></span>
<span id="cb41-27"></span>
<span id="cb41-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">For each of the above scoring categories, provide specific and actionable improvements. Follow these instructions:</span></span>
<span id="cb41-29"></span>
<span id="cb41-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Keep each suggestion concise and mention the slide number(s) if applicable.</span></span>
<span id="cb41-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Do not invent issues and only suggest improvements when the content would clearly benefit from them.</span></span>
<span id="cb41-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- For each catogory, estimate what the new score would be if these improvements are implemented.</span></span>
<span id="cb41-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Return the improvement and new score as part of the response for that category.</span></span>
<span id="cb41-34"></span>
<span id="cb41-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Always return your answer as a JSON object with the following structure:</span></span>
<span id="cb41-36"></span>
<span id="cb41-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb41-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "presentation_title": "",</span></span>
<span id="cb41-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "total_slides": 0,</span></span>
<span id="cb41-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "percent_with_code": 0,</span></span>
<span id="cb41-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "percent_with_images": 0,</span></span>
<span id="cb41-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "estimated_duration_minutes": 0,</span></span>
<span id="cb41-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "tone": "",</span></span>
<span id="cb41-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "clarity": {</span></span>
<span id="cb41-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb41-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "relevance": {</span></span>
<span id="cb41-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb41-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "visual_design": {</span></span>
<span id="cb41-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb41-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "engagement": {</span></span>
<span id="cb41-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-66"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb41-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "pacing": {</span></span>
<span id="cb41-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-73"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb41-74"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "structure": {</span></span>
<span id="cb41-75"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-76"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-77"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-78"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-79"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb41-80"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "consistency": {</span></span>
<span id="cb41-81"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-82"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-83"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-84"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-85"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  },</span></span>
<span id="cb41-86"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  "accessibility": {</span></span>
<span id="cb41-87"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score": 0,</span></span>
<span id="cb41-88"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "justification": "",</span></span>
<span id="cb41-89"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "improvements": "",</span></span>
<span id="cb41-90"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    "score_after_improvements": 0</span></span>
<span id="cb41-91"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  }</span></span>
<span id="cb41-92"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-9-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-9-1" aria-controls="tabset-9-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-9-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-9-2" aria-controls="tabset-9-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-9-1" class="tab-pane active" aria-labelledby="tabset-9-1-tab">
<div class="sourceCode" id="cb42" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb42-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb42-2">system_prompt_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-with-improvements.md"</span>)</span>
<span id="cb42-3"></span>
<span id="cb42-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb42-5">system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb42-6">    system_prompt_file,</span>
<span id="cb42-7">    variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb42-8">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: audience_content,</span>
<span id="cb42-9">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: length_content,</span>
<span id="cb42-10">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: type_content,</span>
<span id="cb42-11">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: event_content,</span>
<span id="cb42-12">    },</span>
<span id="cb42-13">)</span>
<span id="cb42-14"></span>
<span id="cb42-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb42-16">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb42-17">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb42-18">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb42-19">)</span>
<span id="cb42-20"></span>
<span id="cb42-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb42-22">chat.chat(interpolate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>))</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show complete code</summary>
<div class="sourceCode cell-code" id="cb43" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb43-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic, interpolate_file, interpolate</span>
<span id="cb43-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> subprocess</span>
<span id="cb43-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb43-4"></span>
<span id="cb43-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown</span></span>
<span id="cb43-6">subprocess.run(</span>
<span id="cb43-7">    [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>]</span>
<span id="cb43-8">)</span>
<span id="cb43-9"></span>
<span id="cb43-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb43-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb43-12">audience_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb43-13">length_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb43-14">type_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb43-15">event_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb43-16"></span>
<span id="cb43-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb43-18">markdown_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span>)</span>
<span id="cb43-19">markdown_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> markdown_file.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb43-20"></span>
<span id="cb43-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb43-22">system_prompt_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-with-improvements.md"</span>)</span>
<span id="cb43-23"></span>
<span id="cb43-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb43-25">system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb43-26">    system_prompt_file,</span>
<span id="cb43-27">    variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb43-28">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: audience_content,</span>
<span id="cb43-29">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: length_content,</span>
<span id="cb43-30">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: type_content,</span>
<span id="cb43-31">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: event_content,</span>
<span id="cb43-32">    },</span>
<span id="cb43-33">)</span>
<span id="cb43-34"></span>
<span id="cb43-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb43-36">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb43-37">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb43-38">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb43-39">)</span>
<span id="cb43-40"></span>
<span id="cb43-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb43-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb43-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb43-44">chat.chat(interpolate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>))</span></code></pre></div>
</details>
</div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb44" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb44-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb44-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  {                                                                                                  </span></span>
<span id="cb44-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "presentation_title": "The Shiny Side of LLMs",                                                  </span></span>
<span id="cb44-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "total_slides": 16,                                                                              </span></span>
<span id="cb44-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "percent_with_code": 43.75,                                                                      </span></span>
<span id="cb44-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "percent_with_images": 6.25,                                                                     </span></span>
<span id="cb44-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "estimated_duration_minutes": 23,                                                                </span></span>
<span id="cb44-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "tone": "Casual, enthusiastic, and developer-friendly with emojis and informal language",        </span></span>
<span id="cb44-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "clarity": {                                                                                     </span></span>
<span id="cb44-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 7,                                                                                    </span></span>
<span id="cb44-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Content is generally clear with good explanations of technical concepts, but </span></span>
<span id="cb44-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> some slides lack necessary context or detail for the intended audience",                           </span></span>
<span id="cb44-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Define 'chatlas' and 'ellmer' when first introduced (slides 5-6). Add brief   </span></span>
<span id="cb44-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> explanation of what 'system_prompt' does. Explain what 'reactive programming model' means in       </span></span>
<span id="cb44-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> practical terms (slide 7).",                                                                       </span></span>
<span id="cb44-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 8                                                                  </span></span>
<span id="cb44-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                               </span></span>
<span id="cb44-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "relevance": {                                                                                   </span></span>
<span id="cb44-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 8,                                                                                    </span></span>
<span id="cb44-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Very relevant for Python and R users interested in AI/LLMs. Code examples ar </span></span>
<span id="cb44-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> appropriate and the practical focus matches audience needs",                                       </span></span>
<span id="cb44-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Add a brief comparison of when to choose Python vs R approach (slide 4).      </span></span>
<span id="cb44-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Include mention of cost considerations for API usage in slide 2.",                                 </span></span>
<span id="cb44-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 9                                                                  </span></span>
<span id="cb44-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                               </span></span>
<span id="cb44-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "visual_design": {                                                                               </span></span>
<span id="cb44-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 5,                                                                                    </span></span>
<span id="cb44-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Heavy text density on many slides, particularly slides with code blocks.     </span></span>
<span id="cb44-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Limited visual elements beyond one GIF",                                                           </span></span>
<span id="cb44-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Break up text-heavy slides (2, 14) into bullet points or multiple slides. Add </span></span>
<span id="cb44-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> diagrams showing app architecture (slides 4, 7). Use consistent formatting for code blocks and     </span></span>
<span id="cb44-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> ensure proper syntax highlighting.",                                                               </span></span>
<span id="cb44-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 7                                                                  </span></span>
<span id="cb44-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                               </span></span>
<span id="cb44-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "engagement": {                                                                                  </span></span>
<span id="cb44-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 6,                                                                                    </span></span>
<span id="cb44-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Casual tone and emojis add personality, and the practical demo concept is    </span></span>
<span id="cb44-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> engaging, but lacks interactive elements or storytelling",                                         </span></span>
<span id="cb44-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Add a brief demo video or screenshots of the final app. Include a 'What you'l </span></span>
<span id="cb44-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> learn today' slide early on. Add audience poll question about their LLM experience (slide 2).",    </span></span>
<span id="cb44-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 8                                                                  </span></span>
<span id="cb44-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                               </span></span>
<span id="cb44-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "pacing": {                                                                                      </span></span>
<span id="cb44-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 4,                                                                                    </span></span>
<span id="cb44-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Presentation is too long for 10-minute slot (estimated 23 minutes) with unev </span></span>
<span id="cb44-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> distribution - some slides very dense, others very light",                                         </span></span>
<span id="cb44-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Combine slides 5-6 into one comparison slide. Merge slides 8-9 into single    </span></span>
<span id="cb44-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Python/R comparison. Remove or significantly shorten slide 14. Condense setup requirements (slide  </span></span>
<span id="cb44-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> 2).",                                                                                              </span></span>
<span id="cb44-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 7                                                                  </span></span>
<span id="cb44-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                               </span></span>
<span id="cb44-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "structure": {                                                                                   </span></span>
<span id="cb44-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 7,                                                                                    </span></span>
<span id="cb44-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Good logical flow from introduction to implementation, but missing clear     </span></span>
<span id="cb44-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> conclusion and call-to-action",                                                                    </span></span>
<span id="cb44-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Add agenda/outline slide after slide 1. Include key takeaways and resources   </span></span>
<span id="cb44-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> slide before 'Thank you'. Make slide 15 more specific about next steps.",                          </span></span>
<span id="cb44-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 8                                                                  </span></span>
<span id="cb44-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                               </span></span>
<span id="cb44-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "consistency": {                                                                                 </span></span>
<span id="cb44-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 8,                                                                                    </span></span>
<span id="cb44-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Generally consistent tone and formatting throughout, with good parallel      </span></span>
<span id="cb44-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> structure between Python and R examples",                                                          </span></span>
<span id="cb44-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Ensure consistent code block formatting and indentation. Standardize bullet   </span></span>
<span id="cb44-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> point styles across all slides.",                                                                  </span></span>
<span id="cb44-66"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 9                                                                  </span></span>
<span id="cb44-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   },                                                                                               </span></span>
<span id="cb44-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   "accessibility": {                                                                               </span></span>
<span id="cb44-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score": 6,                                                                                    </span></span>
<span id="cb44-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "justification": "Code examples may have small font sizes, and some slides are text-heavy whic </span></span>
<span id="cb44-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> could be challenging for some viewers",                                                            </span></span>
<span id="cb44-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "improvements": "Ensure code font size is at least 14pt. Add alt text description for the GIF  </span></span>
<span id="cb44-73"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> (slide 11). Break up dense paragraphs with more white space and bullet points.",                   </span></span>
<span id="cb44-74"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     "score_after_improvements": 8                                                                  </span></span>
<span id="cb44-75"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">   }                                                                                                </span></span>
<span id="cb44-76"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb44-77"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span> </span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-9-2" class="tab-pane" aria-labelledby="tabset-9-2-tab">
<div class="sourceCode" id="cb45" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb45-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb45-2">system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-with-improvements.md"</span></span>
<span id="cb45-3"></span>
<span id="cb45-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb45-5">system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb45-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb45-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> audience_content,</span>
<span id="cb45-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> length_content,</span>
<span id="cb45-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_content,</span>
<span id="cb45-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> event_content</span>
<span id="cb45-11">)</span>
<span id="cb45-12"></span>
<span id="cb45-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb45-14">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb45-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb45-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt</span>
<span id="cb45-17">)</span>
<span id="cb45-18"></span>
<span id="cb45-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb45-20">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb45-21">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb45-22">))</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show complete code</summary>
<div class="sourceCode cell-code" id="cb46" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb46-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb46-2"></span>
<span id="cb46-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown</span></span>
<span id="cb46-4">quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb46-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>,</span>
<span id="cb46-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span></span>
<span id="cb46-7">)</span>
<span id="cb46-8"></span>
<span id="cb46-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb46-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb46-11">audience_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb46-12">length_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb46-13">type_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb46-14">event_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb46-15"></span>
<span id="cb46-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb46-17">markdown_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span></span>
<span id="cb46-18">markdown_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(markdown_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(markdown_file))</span>
<span id="cb46-19"></span>
<span id="cb46-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb46-21">system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-with-improvements.md"</span></span>
<span id="cb46-22"></span>
<span id="cb46-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb46-24">system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb46-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb46-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> audience_content,</span>
<span id="cb46-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> length_content,</span>
<span id="cb46-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_content,</span>
<span id="cb46-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> event_content</span>
<span id="cb46-30">)</span>
<span id="cb46-31"></span>
<span id="cb46-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb46-33">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb46-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb46-35">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt</span>
<span id="cb46-36">)</span>
<span id="cb46-37"></span>
<span id="cb46-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb46-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb46-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb46-41">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb46-42">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb46-43">))</span></code></pre></div>
</details>
</div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb47" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb47-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; {</span></span>
<span id="cb47-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "presentation_title": "The Shiny Side of LLMs",</span></span>
<span id="cb47-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "total_slides": 16,</span></span>
<span id="cb47-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percent_with_code": 37.5,</span></span>
<span id="cb47-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percent_with_images": 6.25,</span></span>
<span id="cb47-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "estimated_duration_minutes": 14,</span></span>
<span id="cb47-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "tone": "Casual, friendly, and practical with a playful touch (emojis, informal </span></span>
<span id="cb47-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; language, and humor)",</span></span>
<span id="cb47-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "clarity": {</span></span>
<span id="cb47-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 7,</span></span>
<span id="cb47-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Content is generally clear with good code examples and </span></span>
<span id="cb47-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; explanations. However, some technical terms could use more definition for the </span></span>
<span id="cb47-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; mixed-technical audience.",</span></span>
<span id="cb47-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Define key terms like 'reactive programming' (slide 7), </span></span>
<span id="cb47-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; explain what API keys/tokens are (slide 2), and clarify what 'system_prompt' does </span></span>
<span id="cb47-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; in the code examples (slides 5-6).",</span></span>
<span id="cb47-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 8</span></span>
<span id="cb47-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb47-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "relevance": {</span></span>
<span id="cb47-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 8,</span></span>
<span id="cb47-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Excellent match for the audience - Python and R users curious</span></span>
<span id="cb47-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; about AI/LLMs. Provides practical, hands-on examples in both languages and focuses </span></span>
<span id="cb47-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; on implementation rather than deep theory.",</span></span>
<span id="cb47-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Add a brief slide explaining what LLMs are and their basic </span></span>
<span id="cb47-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; capabilities for audience members completely new to AI (slide 3 area).",</span></span>
<span id="cb47-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 9</span></span>
<span id="cb47-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb47-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "visual_design": {</span></span>
<span id="cb47-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 5,</span></span>
<span id="cb47-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Heavy reliance on text and code blocks. Most slides are </span></span>
<span id="cb47-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; text-dense with minimal visual elements. Only one image used, and code blocks </span></span>
<span id="cb47-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; dominate several slides.",</span></span>
<span id="cb47-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Add visual diagrams showing the Shiny-LLM integration flow </span></span>
<span id="cb47-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; (slide 4), include screenshots of the final app (slide 3), reduce text density on </span></span>
<span id="cb47-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; slides 13 and 15, and add icons or visual elements to break up text-heavy slides.",</span></span>
<span id="cb47-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 7</span></span>
<span id="cb47-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb47-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "engagement": {</span></span>
<span id="cb47-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 6,</span></span>
<span id="cb47-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Casual tone with emojis and humor helps engagement, but heavy</span></span>
<span id="cb47-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; code blocks and text-dense slides could lose audience attention. The practical </span></span>
<span id="cb47-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 'build along' approach is engaging.",</span></span>
<span id="cb47-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Add interactive moments like 'raise your hand if you've used </span></span>
<span id="cb47-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Shiny before' (slide 7), include a live demo or video clip of the app in action </span></span>
<span id="cb47-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; (slide 11), and break up slide 13 with bullet points or visual elements.",</span></span>
<span id="cb47-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 8</span></span>
<span id="cb47-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb47-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "pacing": {</span></span>
<span id="cb47-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 6,</span></span>
<span id="cb47-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Some slides are too dense (slides 8, 9, 11, 12, 13) while </span></span>
<span id="cb47-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; others are very light (slides 1, 16). The code-heavy slides will take longer to </span></span>
<span id="cb47-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; present than estimated.",</span></span>
<span id="cb47-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Split slide 11 into two slides (one for Python UI, one for </span></span>
<span id="cb47-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; server), break slide 13 into key points with visuals, and add more content to the </span></span>
<span id="cb47-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; thank you slide (slide 16) with contact info or resources.",</span></span>
<span id="cb47-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 8</span></span>
<span id="cb47-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb47-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "structure": {</span></span>
<span id="cb47-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 8,</span></span>
<span id="cb47-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Clear logical flow from introduction to requirements to </span></span>
<span id="cb47-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; implementation to next steps. Good progression from basic concepts to integration. </span></span>
<span id="cb47-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Clear beginning, middle, and end.",</span></span>
<span id="cb47-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Add a brief agenda slide after slide 1 to preview the journey,</span></span>
<span id="cb47-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; and include a 'key takeaways' slide before the thank you slide.",</span></span>
<span id="cb47-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 9</span></span>
<span id="cb47-66"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb47-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "concistency": {</span></span>
<span id="cb47-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 7,</span></span>
<span id="cb47-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Generally consistent tone and formatting. Code blocks are </span></span>
<span id="cb47-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; well-formatted. Some inconsistency in slide density and the mix of casual/technical</span></span>
<span id="cb47-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; language.",</span></span>
<span id="cb47-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Standardize slide titles (some are questions, some </span></span>
<span id="cb47-73"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; statements), ensure consistent bullet point formatting across slides, and maintain </span></span>
<span id="cb47-74"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; consistent emoji usage throughout.",</span></span>
<span id="cb47-75"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 8</span></span>
<span id="cb47-76"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   },</span></span>
<span id="cb47-77"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "accessibility": {</span></span>
<span id="cb47-78"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score": 6,</span></span>
<span id="cb47-79"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "justification": "Code blocks may be challenging to read for some viewers. </span></span>
<span id="cb47-80"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Heavy text density on several slides could be overwhelming. No obvious color </span></span>
<span id="cb47-81"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; contrast issues mentioned.",</span></span>
<span id="cb47-82"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "improvements": "Increase font size in code blocks (slides 5-6, 8-9, 11-12), </span></span>
<span id="cb47-83"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; add alt text descriptions for the GIF image (slide 10), reduce text density on </span></span>
<span id="cb47-84"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; slide 13, and ensure sufficient white space between elements.",</span></span>
<span id="cb47-85"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     "score_after_improvements": 8</span></span>
<span id="cb47-86"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   }</span></span>
<span id="cb47-87"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; }</span></span></code></pre></div>
</details>
</div>
</div>
</div>
</div>
<p>And of course Claude believes that all the suggested improvements increase the relevant scores. However, don’t be surprised if the numbers are off. Remember from <a href="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/">part 1</a> that it’s just a model that predicts a next token. It’s good to do some sanity checks.</p>
</section>
<section id="ensuring-structured-and-consistent-output" class="level1">
<h1>Ensuring structured and consistent output</h1>
<p>As mentioned previously, specifying an output format makes your outcomes better. That’s why we included: <em>“Please extract the following information… Return your answer as a JSON object with the following structure…”</em> in our prompt. Formats like JSON, YAML, and Markdown tables are common, and JSON tends to be a safe bet for both Python and R. It’s clean, structured, and easy to parse.</p>
<p>And even though we’re explicitly guiding the model toward structured JSON output, we don’t always get what we expect: the model might wrap its output in a code block (e.g., ```json), or forget a comma, or produce something that looks like JSON but isn’t quite valid. That’s weird, right? It’s because specifying the format in the prompt is essentially giving the model strong instructions in plain language. You’re still relying on the model to follow your instructions correctly and generate the output in that format. This works reasonably well, but it’s still a natural language generation task under the hood. And as we learned in part I: it’s all just predicting one token after the other. If the probability of a space instead of a comma is higher… Well, goodbye parsable JSON.</p>
<p>So we need something smarter. That’s where something called “structured output” comes in. Structured output refers to LLMs that support structured schemas as part of the API or function call and not just as instructions in the prompt. These tell the model not just what to output, but how to constrain the generation itself. The model generates a structure directly (like a JSON object or typed dictionary), and the output is guaranteed to match that structure.</p>
<p>The difference is:</p>
<ul>
<li>Prompting: “Please follow this format” (model might comply)</li>
<li>Structured output: “You must generate output that fits this exact shape” (enforced)</li>
</ul>
<p>With <code>chatlas</code> and <code>ellmer</code> we can make use of this structured data feature. Bonus: <code>chatlas</code> will convert the response into a structured Python dictionary, and <code>ellmer</code> into an R data structure so it’s immediately ready to use!</p>
<p>To return structured data we need to change two things: call <code>chat_structured()</code> and provide a specification. In this specification we can specify the fields, their type, and add a description for each of them. Optionally, to prevent duplication, we can remove the field descriptions from our prompt, and refer to the “data model” instead:</p>
<div class="sourceCode" id="cb48" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb48-1">...</span>
<span id="cb48-2"></span>
<span id="cb48-3">Always return the result as a JSON object that conforms to the provided data model.</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show full prompt</summary>
<div class="sourceCode cell-code" id="cb49" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb49-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb49-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You are a presentation coach for data scientists that analyses presentation slide decks written in Markdown. </span></span>
<span id="cb49-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract key information, evaluate quality, and return structured feedback that is constructive, focused and practical.</span></span>
<span id="cb49-4"></span>
<span id="cb49-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The presentation you are helping with is a {{ length }}-minute {{ type }} at {{ event }}.  </span></span>
<span id="cb49-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The audience is {{ audience }}. </span></span>
<span id="cb49-7"></span>
<span id="cb49-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb49-9"></span>
<span id="cb49-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. The presentation title</span></span>
<span id="cb49-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Total number of slides</span></span>
<span id="cb49-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Percentage of slides containing code blocks</span></span>
<span id="cb49-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Percentage of slides containing images</span></span>
<span id="cb49-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Estimated presentation length (in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide)</span></span>
<span id="cb49-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Tone (a brief description)</span></span>
<span id="cb49-16"></span>
<span id="cb49-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span></span>
<span id="cb49-18"></span>
<span id="cb49-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. Clarity of content: evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?</span></span>
<span id="cb49-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Relevance for intended audience: assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?</span></span>
<span id="cb49-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Visual design: judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?</span></span>
<span id="cb49-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Engagement: estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?</span></span>
<span id="cb49-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Pacing: analyze the distribution of content across slides. Are some slides too dense or too light? </span></span>
<span id="cb49-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Structure: review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?</span></span>
<span id="cb49-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">7. consistency: evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?</span></span>
<span id="cb49-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">8. Accessibility: consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?</span></span>
<span id="cb49-27"></span>
<span id="cb49-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">For each of the above scoring categories, provide specific and actionable improvements. Follow these instructions:</span></span>
<span id="cb49-29"></span>
<span id="cb49-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Keep each suggestion concise and mention the slide number(s) if applicable.</span></span>
<span id="cb49-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Do not invent issues and only suggest improvements when the content would clearly benefit from them.</span></span>
<span id="cb49-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- For each catogory, estimate what the new score would be if these improvements are implemented.</span></span>
<span id="cb49-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Return the improvement and new score as part of the response for that category.</span></span>
<span id="cb49-34"></span>
<span id="cb49-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Always return the result as a JSON object that conforms to the provided data model.</span></span>
<span id="cb49-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Double work or Don’t Repeat Yourself (DRY)?
</div>
</div>
<div class="callout-body-container callout-body">
<p>This approach assumes the model actually has access to the data model definition, either via a tool, API schema, or injected example. Without that, results may be inconsistent. It seems that Claude’s results are quite good this way, but it may vary for other models, so it’s good to try different things.</p>
<p>For educational purposes it’s nice to see that the LLM is getting the required information from the provided data model, and not from the system prompt.</p>
<p>Having both the description in the prompt and in a data model is an option to, especially when:</p>
<ul>
<li>You want strong guidance and fallback validation.</li>
<li>You’re building for reliability or multi-model use (not all models treat schemas equally).</li>
<li>You’re iterating and want to troubleshoot what part is helping.</li>
</ul>
</div>
</div>
<p>We’ll come back to why the Don’t-Repeat-Yourself (DRY) principle might be worthwhile for the token count and cost too.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-10-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-10-1" aria-controls="tabset-10-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-10-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-10-2" aria-controls="tabset-10-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-10-1" class="tab-pane active" aria-labelledby="tabset-10-1-tab">
<p>To use structured output, you give the model some input and a <a href="https://docs.pydantic.dev/latest/concepts/models/">Pydantic model</a> that describes the kind of data you want. The Pydantic model is like a form with specific fields and types, like <code>title: string</code> or <code>duration: integer</code>.</p>
<div class="sourceCode" id="cb50" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb50-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define data structure to extract from the input</span></span>
<span id="cb50-2">ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)]</span>
<span id="cb50-3">PercentType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100.0</span>)]</span>
<span id="cb50-4">MinutesType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb50-5">SlideCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb50-6"></span>
<span id="cb50-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> ScoringCategory(BaseModel):</span>
<span id="cb50-8">    score: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1–10."</span>)</span>
<span id="cb50-9">    justification: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span>)</span>
<span id="cb50-10">    improvements: Optional[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-11">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>,</span>
<span id="cb50-12">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb50-13">    )</span>
<span id="cb50-14">    score_after_improvements: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-15">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb50-16">    )</span>
<span id="cb50-17"></span>
<span id="cb50-18"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> DeckAnalysis(BaseModel):</span>
<span id="cb50-19">    presentation_title: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>)</span>
<span id="cb50-20">    total_slides: SlideCount</span>
<span id="cb50-21">    percent_with_code: PercentType</span>
<span id="cb50-22">    percent_with_images: PercentType</span>
<span id="cb50-23">    estimated_duration_minutes: MinutesType</span>
<span id="cb50-24">    tone: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-25">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the tone of the presentation."</span></span>
<span id="cb50-26">    )</span>
<span id="cb50-27"></span>
<span id="cb50-28">    clarity: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-29">        ...,</span>
<span id="cb50-30">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb50-31">    )</span>
<span id="cb50-32">    relevance: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-33">        ...,</span>
<span id="cb50-34">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb50-35">    )</span>
<span id="cb50-36">    visual_design: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-37">        ...,</span>
<span id="cb50-38">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb50-39">    )</span>
<span id="cb50-40">    engagement: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-41">        ...,</span>
<span id="cb50-42">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb50-43">    )</span>
<span id="cb50-44">    pacing: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-45">        ...,</span>
<span id="cb50-46">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb50-47">    )</span>
<span id="cb50-48">    structure: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-49">        ...,</span>
<span id="cb50-50">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb50-51">    )</span>
<span id="cb50-52">    concistency: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># spelling kept as-is</span></span>
<span id="cb50-53">        ...,</span>
<span id="cb50-54">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb50-55">    )</span>
<span id="cb50-56">    accessibility: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb50-57">        ...,</span>
<span id="cb50-58">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb50-59">    )</span>
<span id="cb50-60"></span>
<span id="cb50-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb50-62">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb50-63">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb50-64">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb50-65">)</span>
<span id="cb50-66"></span>
<span id="cb50-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb50-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb50-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb50-70">chat.chat_structured(</span>
<span id="cb50-71">    interpolate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>),</span>
<span id="cb50-72">    data_model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>DeckAnalysis,</span>
<span id="cb50-73">)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show complete code</summary>
<div class="sourceCode cell-code" id="cb51" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb51-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic, interpolate_file, interpolate</span>
<span id="cb51-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> subprocess</span>
<span id="cb51-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb51-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pydantic <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> BaseModel, Field</span>
<span id="cb51-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> typing <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Annotated, Optional</span>
<span id="cb51-6"></span>
<span id="cb51-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown</span></span>
<span id="cb51-8">subprocess.run(</span>
<span id="cb51-9">    [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>]</span>
<span id="cb51-10">)</span>
<span id="cb51-11"></span>
<span id="cb51-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb51-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb51-14">audience_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb51-15">length_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb51-16">type_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb51-17">event_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb51-18"></span>
<span id="cb51-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb51-20">markdown_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span>)</span>
<span id="cb51-21">markdown_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> markdown_file.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb51-22"></span>
<span id="cb51-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb51-24">system_prompt_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-structured.md"</span>)</span>
<span id="cb51-25"></span>
<span id="cb51-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb51-27">system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb51-28">    system_prompt_file,</span>
<span id="cb51-29">    variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb51-30">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: audience_content,</span>
<span id="cb51-31">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: length_content,</span>
<span id="cb51-32">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: type_content,</span>
<span id="cb51-33">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: event_content,</span>
<span id="cb51-34">    },</span>
<span id="cb51-35">)</span>
<span id="cb51-36"></span>
<span id="cb51-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define data structure to extract from the input</span></span>
<span id="cb51-38">ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)]</span>
<span id="cb51-39">PercentType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100.0</span>)]</span>
<span id="cb51-40">MinutesType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb51-41">SlideCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb51-42"></span>
<span id="cb51-43"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> ScoringCategory(BaseModel):</span>
<span id="cb51-44">    score: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1–10."</span>)</span>
<span id="cb51-45">    justification: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span>)</span>
<span id="cb51-46">    improvements: Optional[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-47">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>,</span>
<span id="cb51-48">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb51-49">    )</span>
<span id="cb51-50">    score_after_improvements: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-51">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb51-52">    )</span>
<span id="cb51-53"></span>
<span id="cb51-54"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> DeckAnalysis(BaseModel):</span>
<span id="cb51-55">    presentation_title: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>)</span>
<span id="cb51-56">    total_slides: SlideCount</span>
<span id="cb51-57">    percent_with_code: PercentType</span>
<span id="cb51-58">    percent_with_images: PercentType</span>
<span id="cb51-59">    estimated_duration_minutes: MinutesType</span>
<span id="cb51-60">    tone: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-61">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the tone of the presentation."</span></span>
<span id="cb51-62">    )</span>
<span id="cb51-63"></span>
<span id="cb51-64">    clarity: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-65">        ...,</span>
<span id="cb51-66">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb51-67">    )</span>
<span id="cb51-68">    relevance: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-69">        ...,</span>
<span id="cb51-70">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb51-71">    )</span>
<span id="cb51-72">    visual_design: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-73">        ...,</span>
<span id="cb51-74">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb51-75">    )</span>
<span id="cb51-76">    engagement: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-77">        ...,</span>
<span id="cb51-78">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb51-79">    )</span>
<span id="cb51-80">    pacing: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-81">        ...,</span>
<span id="cb51-82">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb51-83">    )</span>
<span id="cb51-84">    structure: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-85">        ...,</span>
<span id="cb51-86">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb51-87">    )</span>
<span id="cb51-88">    concistency: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># spelling kept as-is</span></span>
<span id="cb51-89">        ...,</span>
<span id="cb51-90">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb51-91">    )</span>
<span id="cb51-92">    accessibility: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb51-93">        ...,</span>
<span id="cb51-94">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb51-95">    )</span>
<span id="cb51-96"></span>
<span id="cb51-97"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb51-98">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb51-99">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb51-100">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb51-101">)</span>
<span id="cb51-102"></span>
<span id="cb51-103"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb51-104"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb51-105"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb51-106">chat.chat_structured(</span>
<span id="cb51-107">    interpolate(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>),</span>
<span id="cb51-108">    data_model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>DeckAnalysis,</span>
<span id="cb51-109">)</span></code></pre></div>
</details>
</div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb52" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb52-1">{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'presentation_title'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'The Shiny Side of LLMs'</span>,</span>
<span id="cb52-2"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'total_slides'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>,</span>
<span id="cb52-3"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'percent_with_code'</span>: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">31.25</span>,</span>
<span id="cb52-4"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'percent_with_images'</span>: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6.25</span>,</span>
<span id="cb52-5"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'estimated_duration_minutes'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb52-6"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tone'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Casual and friendly with a teaching approach, using emojis and humor to make technical content approachable'</span>,</span>
<span id="cb52-7"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'clarity'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>,</span>
<span id="cb52-8">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Good step-by-step approach and clear explanations, but heavy code blocks may confuse non-technical audience members. Key concepts are well-defined.'</span>,</span>
<span id="cb52-9">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Simplify code examples on slides 5-6, 8-9, 11-12 to show only essential lines with comments. Add brief explanations of technical terms like 'reactive programming' on slide 7."</span>,</span>
<span id="cb52-10">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb52-11"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'relevance'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,</span>
<span id="cb52-12">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Highly relevant to the posit::conf audience with practical Python and R examples. Addresses real need for AI integration in data science workflows.'</span>,</span>
<span id="cb52-13">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Add a brief mention of common use cases data scientists might have for LLM-powered apps beyond presentation feedback (slide 3 or 14).'</span>,</span>
<span id="cb52-14">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb52-15"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'visual_design'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb52-16">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Very text-heavy with large code blocks that will be difficult to read in a presentation setting. Limited visual hierarchy and only one image/GIF.'</span>,</span>
<span id="cb52-17">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Replace full code blocks on slides 5-6, 8-9, 11-12 with key snippets only. Add diagrams showing the app architecture on slide 4. Include screenshots of DeckCheck app on slide 3.'</span>,</span>
<span id="cb52-18">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>},</span>
<span id="cb52-19"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'engagement'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,</span>
<span id="cb52-20">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Good use of humor, emojis, and relatable examples (DeckCheck concept). The magic GIF adds personality. However, code-heavy sections may lose audience attention.'</span>,</span>
<span id="cb52-21">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Add interactive elements or polls. Include a brief live demo or recorded demo on slide 10 instead of the GIF. Show before/after examples of presentation feedback.'</span>,</span>
<span id="cb52-22">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb52-23"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pacing'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>,</span>
<span id="cb52-24">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Uneven distribution with slides 5-6, 8-9, 11-12 being too dense for a 10-minute talk. Some slides like slide 1 and 15 are very light.'</span>,</span>
<span id="cb52-25">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Condense setup information from slides 2-4 into 2 slides. Reduce code examples to key snippets. Combine slides 1 and 2 into a single introduction slide.'</span>,</span>
<span id="cb52-26">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>},</span>
<span id="cb52-27"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'structure'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,</span>
<span id="cb52-28">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Logical flow from introduction through setup, basic concepts, and implementation. Clear progression from simple to complex. Good use of the DeckCheck example throughout.'</span>,</span>
<span id="cb52-29">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Add an agenda slide after slide 1. Create clearer section breaks between setup (slides 2-4), concepts (slides 5-7), and implementation (slides 8-12).'</span>,</span>
<span id="cb52-30">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb52-31"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'concistency'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,</span>
<span id="cb52-32">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Consistent tone, formatting, and use of the DeckCheck example throughout. Parallel structure for Python and R examples works well.'</span>,</span>
<span id="cb52-33">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>,</span>
<span id="cb52-34">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb52-35"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'accessibility'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb52-36">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Code blocks will be difficult to read for audience members, especially those with visual challenges. Font sizes in code examples are likely too small for presentation setting.'</span>,</span>
<span id="cb52-37">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Use larger fonts and fewer lines in code examples (slides 5-6, 8-9, 11-12). Add high contrast between text and background. Include alt text descriptions for the GIF on slide 10.'</span>,</span>
<span id="cb52-38">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>}</span></code></pre></div>
</details>
</div>
<p>The code above defines a set of Pydantic models that describe the expected structure of our output.<code>ScoringCategory</code> is a reusable model containing a score (an integer constrained between 0 and 10), a justification string, optional improvement suggestions, and an estimated score after implementing the improvements. Numeric fields in the top-level model, <code>DeckAnalysis</code>, are validated using <code>Annotated</code> types with constraints defined via <code>Field(...)</code> . For example, to ensure percentages are between 0.0 and 100.0, and counts are non-negative. The <code>DeckAnalysis</code> model nests eight <code>ScoringCategory</code> models. It saves some duplication. The top-level model is given to the <code>chat_structured()</code> method: <code>chat.chat_structured(interpolate("Here are the slides in Markdown: {{ markdown_content }}"), data_model=DeckAnalysis)</code> .</p>
</div>
<div id="tabset-10-2" class="tab-pane" aria-labelledby="tabset-10-2-tab">
<p>To use structured output, you use <code>$chat_structured()</code> instead of the <code>$chat()</code>method. You’ll also need to define a type specification that describes the structure of the data that you want.</p>
<div class="sourceCode" id="cb53" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb53-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reusable scoring category</span></span>
<span id="cb53-2">type_scoring_category <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb53-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb53-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1 to 10."</span></span>
<span id="cb53-5">  ),</span>
<span id="cb53-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">justification =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb53-7">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span></span>
<span id="cb53-8">  ),</span>
<span id="cb53-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb53-10">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb53-11">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb53-12">  ),</span>
<span id="cb53-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score_after_improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb53-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb53-15">  )</span>
<span id="cb53-16">)</span>
<span id="cb53-17"></span>
<span id="cb53-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Top-level deck analysis object</span></span>
<span id="cb53-19">type_deck_analysis <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb53-20">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">presentation_title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>),</span>
<span id="cb53-21">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_slides =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total number of slides."</span>),</span>
<span id="cb53-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb53-23">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing code blocks (0–100)."</span></span>
<span id="cb53-24">  ),</span>
<span id="cb53-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_images =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb53-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing images (0–100)."</span></span>
<span id="cb53-27">  ),</span>
<span id="cb53-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">estimated_duration_minutes =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb53-29">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated presentation length in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide."</span></span>
<span id="cb53-30">  ),</span>
<span id="cb53-31">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tone =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb53-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the presentation tone (e.g., informal, technical, playful)."</span></span>
<span id="cb53-33">  ),</span>
<span id="cb53-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">clarity =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-35">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb53-36">    type_scoring_category</span>
<span id="cb53-37">  ),</span>
<span id="cb53-38">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">relevance =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-39">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Asses how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb53-40">    type_scoring_category</span>
<span id="cb53-41">  ),</span>
<span id="cb53-42">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">visual_design =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-43">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb53-44">    type_scoring_category</span>
<span id="cb53-45">  ),</span>
<span id="cb53-46">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">engagement =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-47">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb53-48">    type_scoring_category</span>
<span id="cb53-49">  ),</span>
<span id="cb53-50">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pacing =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-51">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb53-52">    type_scoring_category</span>
<span id="cb53-53">  ),</span>
<span id="cb53-54">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">structure =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-55">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb53-56">    type_scoring_category</span>
<span id="cb53-57">  ),</span>
<span id="cb53-58">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">concistency =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-59">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb53-60">    type_scoring_category</span>
<span id="cb53-61">  ),</span>
<span id="cb53-62">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accessibility =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb53-63">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb53-64">    type_scoring_category</span>
<span id="cb53-65">  )</span>
<span id="cb53-66">)</span>
<span id="cb53-67"></span>
<span id="cb53-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb53-69">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb53-70">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb53-71">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt</span>
<span id="cb53-72">)</span>
<span id="cb53-73"></span>
<span id="cb53-74"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb53-75"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb53-76"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb53-77">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_structured</span>(</span>
<span id="cb53-78">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb53-79">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb53-80">  ),</span>
<span id="cb53-81">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_deck_analysis</span>
<span id="cb53-82">)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show complete code</summary>
<div class="sourceCode cell-code" id="cb54" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb54-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb54-2"></span>
<span id="cb54-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown</span></span>
<span id="cb54-4">quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb54-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>,</span>
<span id="cb54-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span></span>
<span id="cb54-7">)</span>
<span id="cb54-8"></span>
<span id="cb54-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb54-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb54-11">audience_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb54-12">length_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb54-13">type_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb54-14">event_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb54-15"></span>
<span id="cb54-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb54-17">markdown_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span></span>
<span id="cb54-18">markdown_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(markdown_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(markdown_file))</span>
<span id="cb54-19"></span>
<span id="cb54-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb54-21">system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-structured.md"</span></span>
<span id="cb54-22"></span>
<span id="cb54-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb54-24">system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb54-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb54-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> audience_content,</span>
<span id="cb54-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> length_content,</span>
<span id="cb54-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_content,</span>
<span id="cb54-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> event_content</span>
<span id="cb54-30">)</span>
<span id="cb54-31"></span>
<span id="cb54-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reusable scoring category</span></span>
<span id="cb54-33">type_scoring_category <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb54-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb54-35">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1 to 10."</span></span>
<span id="cb54-36">  ),</span>
<span id="cb54-37">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">justification =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb54-38">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span></span>
<span id="cb54-39">  ),</span>
<span id="cb54-40">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb54-41">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb54-42">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb54-43">  ),</span>
<span id="cb54-44">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score_after_improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb54-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb54-46">  )</span>
<span id="cb54-47">)</span>
<span id="cb54-48"></span>
<span id="cb54-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Top-level deck analysis object</span></span>
<span id="cb54-50">type_deck_analysis <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb54-51">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">presentation_title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>),</span>
<span id="cb54-52">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_slides =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total number of slides."</span>),</span>
<span id="cb54-53">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb54-54">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing code blocks (0–100)."</span></span>
<span id="cb54-55">  ),</span>
<span id="cb54-56">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_images =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb54-57">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing images (0–100)."</span></span>
<span id="cb54-58">  ),</span>
<span id="cb54-59">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">estimated_duration_minutes =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb54-60">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated presentation length in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide."</span></span>
<span id="cb54-61">  ),</span>
<span id="cb54-62">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tone =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb54-63">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the presentation tone (e.g., informal, technical, playful)."</span></span>
<span id="cb54-64">  ),</span>
<span id="cb54-65">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">clarity =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-66">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb54-67">    type_scoring_category</span>
<span id="cb54-68">  ),</span>
<span id="cb54-69">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">relevance =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-70">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Asses how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb54-71">    type_scoring_category</span>
<span id="cb54-72">  ),</span>
<span id="cb54-73">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">visual_design =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-74">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb54-75">    type_scoring_category</span>
<span id="cb54-76">  ),</span>
<span id="cb54-77">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">engagement =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-78">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb54-79">    type_scoring_category</span>
<span id="cb54-80">  ),</span>
<span id="cb54-81">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pacing =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-82">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb54-83">    type_scoring_category</span>
<span id="cb54-84">  ),</span>
<span id="cb54-85">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">structure =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-86">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb54-87">    type_scoring_category</span>
<span id="cb54-88">  ),</span>
<span id="cb54-89">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">concistency =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-90">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb54-91">    type_scoring_category</span>
<span id="cb54-92">  ),</span>
<span id="cb54-93">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accessibility =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb54-94">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb54-95">    type_scoring_category</span>
<span id="cb54-96">  )</span>
<span id="cb54-97">)</span>
<span id="cb54-98"></span>
<span id="cb54-99"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb54-100">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb54-101">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb54-102">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt</span>
<span id="cb54-103">)</span>
<span id="cb54-104"></span>
<span id="cb54-105"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb54-106"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Since all the instructions are in the system prompt, we can just</span></span>
<span id="cb54-107"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># provide the Markdown content as a message</span></span>
<span id="cb54-108">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_structured</span>(</span>
<span id="cb54-109">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb54-110">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb54-111">  ),</span>
<span id="cb54-112">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_deck_analysis</span>
<span id="cb54-113">)</span></code></pre></div>
</details>
</div>
<div class="cell">
<details class="code-fold">
<summary>Show output</summary>
<div class="sourceCode cell-code" id="cb55" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb55-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $presentation_title</span></span>
<span id="cb55-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "The Shiny Side of LLMs"</span></span>
<span id="cb55-3"></span>
<span id="cb55-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $total_slides</span></span>
<span id="cb55-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 16</span></span>
<span id="cb55-6"></span>
<span id="cb55-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $percent_with_code</span></span>
<span id="cb55-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 56.25</span></span>
<span id="cb55-9"></span>
<span id="cb55-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $percent_with_images</span></span>
<span id="cb55-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 6.25</span></span>
<span id="cb55-12"></span>
<span id="cb55-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $estimated_duration_minutes</span></span>
<span id="cb55-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 12</span></span>
<span id="cb55-15"></span>
<span id="cb55-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $tone</span></span>
<span id="cb55-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "Informal, friendly, and technical with playful elements"</span></span>
<span id="cb55-18"></span>
<span id="cb55-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $clarity</span></span>
<span id="cb55-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     7</span></span>
<span id="cb55-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Content is generally clear with good step-by-step explanations, but some technical concepts could benefit from more context for non-technical audience members.</span></span>
<span id="cb55-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add brief explanations of what Shiny and LLMs are in slides 2-3. Define technical terms like 'reactive programming' in slide 7. Provide more context about API keys and authentication in slide 2.</span></span>
<span id="cb55-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span>
<span id="cb55-28"></span>
<span id="cb55-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $relevance</span></span>
<span id="cb55-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     8</span></span>
<span id="cb55-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Content is highly relevant to the Python and R user audience at posit::conf, with practical examples in both languages. Good mix of beginner-friendly and intermediate concepts.</span></span>
<span id="cb55-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add a brief slide explaining why LLMs are relevant for data scientists specifically. Include more context about when to use LLM-powered apps in data science workflows.</span></span>
<span id="cb55-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        9</span></span>
<span id="cb55-38"></span>
<span id="cb55-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $visual_design</span></span>
<span id="cb55-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     6</span></span>
<span id="cb55-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Code blocks are well-formatted, but slides are text-heavy with minimal visual hierarchy. Only one image used throughout the presentation.</span></span>
<span id="cb55-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add visual diagrams showing the app architecture in slides 4-5. Use bullet points and icons to break up text in slides 2 and 15. Consider adding screenshots of the actual app interface.</span></span>
<span id="cb55-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span>
<span id="cb55-48"></span>
<span id="cb55-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $engagement</span></span>
<span id="cb55-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     7</span></span>
<span id="cb55-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Good use of emojis and casual tone keeps it engaging. The magic GIF adds personality, but more interactive elements could enhance engagement.</span></span>
<span id="cb55-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add a live demo or screenshots of the working app. Include audience interaction prompts in slides 11-12. Add visual progress indicators showing the app development journey.</span></span>
<span id="cb55-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span>
<span id="cb55-58"></span>
<span id="cb55-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $pacing</span></span>
<span id="cb55-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     6</span></span>
<span id="cb55-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Some slides are code-heavy (slides 8-9, 12-13) while others are very light (slide 3). The transition from basics to implementation could be smoother.</span></span>
<span id="cb55-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Split the heavy code slides (8-9, 12-13) into smaller chunks. Add transition slides between major sections. Balance slide 14 with more concrete examples.</span></span>
<span id="cb55-66"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        7</span></span>
<span id="cb55-68"></span>
<span id="cb55-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $structure</span></span>
<span id="cb55-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     8</span></span>
<span id="cb55-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-73"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Clear logical flow from introduction to requirements to implementation. Good progression from basic concepts to advanced integration.</span></span>
<span id="cb55-74"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-75"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add an agenda slide after slide 1. Include a recap slide before the conclusion. Make the transition from slide 14 to 15 smoother with a bridge sentence.</span></span>
<span id="cb55-76"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-77"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        9</span></span>
<span id="cb55-78"></span>
<span id="cb55-79"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $concistency</span></span>
<span id="cb55-80"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-81"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     7</span></span>
<span id="cb55-82"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-83"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Generally consistent formatting and tone, but code highlighting varies between Python and R sections. Some inconsistency in slide structure.</span></span>
<span id="cb55-84"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-85"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Ensure consistent code block styling across Python and R examples. Standardize the format for requirement lists in slide 2. Use consistent header styles throughout.</span></span>
<span id="cb55-86"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-87"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span>
<span id="cb55-88"></span>
<span id="cb55-89"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $accessibility</span></span>
<span id="cb55-90"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb55-91"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     7</span></span>
<span id="cb55-92"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; justification</span></span>
<span id="cb55-93"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Good contrast in code blocks and readable font sizes. However, the single GIF lacks alt text description and some code blocks are quite dense.</span></span>
<span id="cb55-94"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; improvements</span></span>
<span id="cb55-95"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add alt text description for the magic GIF in slide 11. Break up dense code blocks in slides 8-9, 12-13. Use larger font sizes for code comments.</span></span>
<span id="cb55-96"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; score_after_improvements</span></span>
<span id="cb55-97"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span></code></pre></div>
</details>
</div>
<p>You can also specify the full schema that you want to get back from the LLM as a JSON schema. This could be handy if you’re not keen on manually converting everything using the <code>type_*()</code>functions (or if an LLM doesn’t provide you with the correct output after asking 6 times). Your friend: <code>type_from_schema()</code>.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p><code>type_object()</code> returns a named list in R. If you want to extract a data frame from a single prompt (e.g.&nbsp;reading information from a markdown table), you can wrap <code>type_object()</code> in <code>type_array()</code> and create an array of objects. We do something similar here for the scoring categories. It can be hard to wrap your head around as an R user. The <code>ellmer</code> documentation contains some more <a href="https://ellmer.tidyverse.org/articles/structured-data.html#data-frames">good examples</a>.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="when-llms-guess-tools-know" class="level1">
<h1>When LLMs guess, tools know</h1>
<p>As you’ve seen in our conversation with Claude so far, LLMs struggle with tasks like counting slides. This is because they don’t actually run code or parse content structurally like a proper parser would. LLMs aren’t doing magic, they are simply predicting the next word based on patterns in text. So when you ask an LLM, “how many slides contain code?”, it’s making an educated guess based on the wording of the slides. It does not actually scan them line-by-line and checking for code blocks. The result: inconsistent answers. In our example, there are 16 slides, 6 with code, and 1 with an image (aka 37.5% code slides and 6.25% image slides). The LLM gets close, but its answers vary and we’re not aiming for “close enough.” The solution: build something on top of the LLM that does this reliably for us. Hello something fancy called “tool calling”!</p>
<p>Everyone talks about tool calling these days, and for a good reason: it’s that extra bit of power you give to an LLM. While it sounds complicated, it’s easy to explain: you let your code handle tasks that the LLM struggles with, like accurately counting how many slides have code or images, and then the LLM uses those exact numbers. You basically help the LLM with its answer. This help comes in the form of a tool (aka a function) that you’ve written and exposed to the model. Of course the LLM needs to know that the tool is there to begin with, and it needs to know how to use it. Luckily <code>chatlas</code> and <code>ellmer</code> can help us doing that.</p>
<p>So how would this work of we want to improve the slide counts for DeckCheck? First, we construct a simple Python or R function that reads our slides, spots code blocks and images, and gives exact percentages. To make our life a little bit easier, we’re opting for the HTML version of Quarto slides here, and not Markdown. In this case, HTML is handy because each slide is neatly contained in a <code>&lt;section&gt;</code> tag, and code is clearly marked with a <code>sourceCode</code> class. For an LLM, HTML is noisy (full of CSS, scripts, and other distractions). But for a parsing function like we’re going to write, it’s straightforward to process. The nice thing about such a function is that you can fully customise it: maybe you only want to count Python or R code chunks only, or ignore very short examples. We call our function <code>calculate_slide_metric</code>:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-11-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-11-1" aria-controls="tabset-11-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-11-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-11-2" aria-controls="tabset-11-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-11-1" class="tab-pane active" aria-labelledby="tabset-11-1-tab">
<div class="sourceCode" id="cb56" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb56-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> calculate_slide_metric(metric: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> Union[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>]:</span>
<span id="cb56-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb56-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb56-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb56-5"></span>
<span id="cb56-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Parameters</span></span>
<span id="cb56-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    ----------</span></span>
<span id="cb56-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    metric : str</span></span>
<span id="cb56-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb56-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        "code" for percentage of slides containing fenced code blocks,</span></span>
<span id="cb56-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        or "images" for percentage of slides containing images.</span></span>
<span id="cb56-12"></span>
<span id="cb56-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Returns</span></span>
<span id="cb56-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    -------</span></span>
<span id="cb56-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    float or int</span></span>
<span id="cb56-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The calculated metric value.</span></span>
<span id="cb56-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb56-18">    html_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.html"</span>)</span>
<span id="cb56-19">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> html_file.exists():</span>
<span id="cb56-20">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">FileNotFoundError</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"HTML file </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>html_file<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> does not exist."</span>)</span>
<span id="cb56-21"></span>
<span id="cb56-22">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb56-23">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(html_file, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"r"</span>, encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> f:</span>
<span id="cb56-24">        html_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> f.read()</span>
<span id="cb56-25"></span>
<span id="cb56-26">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb56-27">    slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> html_content.split(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>)</span>
<span id="cb56-28">    total_slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(slides)</span>
<span id="cb56-29"></span>
<span id="cb56-30">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>:</span>
<span id="cb56-31">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> total_slides</span>
<span id="cb56-32">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>:</span>
<span id="cb56-33">        slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb56-34">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb56-35">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>:</span>
<span id="cb56-36">        slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;img"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb56-37">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb56-38">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb56-39">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">ValueError</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb56-40"></span>
<span id="cb56-41">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> result</span></code></pre></div>
</div>
<div id="tabset-11-2" class="tab-pane" aria-labelledby="tabset-11-2-tab">
<div class="sourceCode" id="cb57" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb57-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb57-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb57-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb57-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param metric The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb57-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' "code" for percentage of slides containing fenced code blocks, or "images"</span></span>
<span id="cb57-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' for percentage of slides containing images.</span></span>
<span id="cb57-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return The calculated metric value.</span></span>
<span id="cb57-8">calculate_slide_metric <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(metric) {</span>
<span id="cb57-9">  html_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.html"</span></span>
<span id="cb57-10">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.exists</span>(html_file)) {</span>
<span id="cb57-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(</span>
<span id="cb57-12">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"HTML file does not exist. Please render your Quarto presentation first."</span></span>
<span id="cb57-13">    )</span>
<span id="cb57-14">  }</span>
<span id="cb57-15">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb57-16">  html_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(html_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(html_file))</span>
<span id="cb57-17"></span>
<span id="cb57-18">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb57-19">  slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strsplit</span>(html_content, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>))</span>
<span id="cb57-20"></span>
<span id="cb57-21">  total_slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(slides)</span>
<span id="cb57-22"></span>
<span id="cb57-23">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>) {</span>
<span id="cb57-24">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> total_slides</span>
<span id="cb57-25">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>) {</span>
<span id="cb57-26">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides where we see the "sourceCode" class</span></span>
<span id="cb57-27">    slides_with_code <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span>, slides))</span>
<span id="cb57-28">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb57-29">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>) {</span>
<span id="cb57-30">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides with image tag</span></span>
<span id="cb57-31">    slides_with_image <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'&lt;img'</span>, slides))</span>
<span id="cb57-32">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb57-33">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> {</span>
<span id="cb57-34">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb57-35">  }</span>
<span id="cb57-36"></span>
<span id="cb57-37">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(result)</span>
<span id="cb57-38">}</span></code></pre></div>
</div>
</div>
</div>
<p>This function is the tool the LLM can call to accurately count slides. It has one input argument: <code>metric</code>. To let the model know that this tool is available, we register it with <code>register_tool</code> before we start talking to the LLM.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Who does the work?
</div>
</div>
<div class="callout-body-container callout-body">
<p>Note that the LLM itself is not going to read the HTML file. The underlying Python or R session that execute the function is.</p>
</div>
</div>
<p>There’s a little catch though: structured data extraction automatically disables tool calling. Hmm… But we used structured data! Luckily you can work around this by doing a regular <code>chat()</code> and then using <code>chat_structured()</code>. We’re basically adding an extra pair of user and assistant turn by dividing one task into two. This means we need to reorganise our system prompt by telling the LLM that there is a first task, and a subsequent task. The first one going to focus on retrieving the meta data, and the second one on extracting scores and improvements. For clarity, the tasks are numbered and have short alias (e.g.&nbsp;“Task 1 (counts)”). This makes it easier to reference them in our user prompts.</p>
<div class="sourceCode" id="cb58" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb58-1">...</span>
<span id="cb58-2"></span>
<span id="cb58-3">You can be asked for one of the following tasks. Each has a number and a name:</span>
<span id="cb58-4"></span>
<span id="cb58-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Task 1 (counts)</span></span>
<span id="cb58-6"></span>
<span id="cb58-7">...</span>
<span id="cb58-8"></span>
<span id="cb58-9">Return only the JSON results, nothing else.</span>
<span id="cb58-10"></span>
<span id="cb58-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Task 2 (suggestions)</span></span>
<span id="cb58-12"></span>
<span id="cb58-13">...</span>
<span id="cb58-14"></span>
<span id="cb58-15">You bundle your results with the results from the first task. Always return the result as a JSON object that conforms to the provided data model.</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show full prompt</summary>
<div class="sourceCode cell-code" id="cb59" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb59-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb59-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You are a presentation coach for data scientists that analyses presentation slide decks written in Markdown. </span></span>
<span id="cb59-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract key information, evaluate quality, and return structured feedback that is constructive, focused and practical.</span></span>
<span id="cb59-4"></span>
<span id="cb59-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The presentation you are helping with is a {{ length }}-minute {{ type }} at {{ event }}.  </span></span>
<span id="cb59-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The audience is {{ audience }}. </span></span>
<span id="cb59-7"></span>
<span id="cb59-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You can be asked for one of the following tasks. Each has a number and a name:</span></span>
<span id="cb59-9"></span>
<span id="cb59-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1 (counts)</span></span>
<span id="cb59-11"></span>
<span id="cb59-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb59-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The number of slides</span></span>
<span id="cb59-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The percentage of slides containing code blocks</span></span>
<span id="cb59-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The percentage of slides containing images </span></span>
<span id="cb59-16"></span>
<span id="cb59-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Return only the JSON results, nothing else.</span></span>
<span id="cb59-18"></span>
<span id="cb59-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2 (suggestions)</span></span>
<span id="cb59-20"></span>
<span id="cb59-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb59-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The presentation title</span></span>
<span id="cb59-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Estimated presentation length (in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide)</span></span>
<span id="cb59-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Tone (a brief description)</span></span>
<span id="cb59-25"></span>
<span id="cb59-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span></span>
<span id="cb59-27"></span>
<span id="cb59-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. Clarity of content: evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?</span></span>
<span id="cb59-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Relevance for intended audience: assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?</span></span>
<span id="cb59-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Visual design: judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?</span></span>
<span id="cb59-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Engagement: estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?</span></span>
<span id="cb59-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Pacing: analyze the distribution of content across slides. Are some slides too dense or too light? </span></span>
<span id="cb59-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Structure: review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?</span></span>
<span id="cb59-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">7. consistency: evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?</span></span>
<span id="cb59-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">8. Accessibility: consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?</span></span>
<span id="cb59-36"></span>
<span id="cb59-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">For each of the above scoring categories, provide specific and actionable improvements. Follow these instructions:</span></span>
<span id="cb59-38"></span>
<span id="cb59-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Keep each suggestion concise and mention the slide number(s) if applicable.</span></span>
<span id="cb59-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Do not invent issues and only suggest improvements when the content would clearly benefit from them.</span></span>
<span id="cb59-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- For each catogory, estimate what the new score would be if these improvements are implemented.</span></span>
<span id="cb59-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Return the improvement and new score as part of the response for that category.</span></span>
<span id="cb59-43"></span>
<span id="cb59-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You bundle your results with the results from the first task. Always return the result as a JSON object that conforms to the provided data model.</span></span>
<span id="cb59-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
<p>Because the <code>chat</code> object stateful (and therefore remembers previous responses), it can use the response from the first prompt for the response of the second one. Handy!</p>
<p>Altogether, this results in the following workflow:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-12-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-12-1" aria-controls="tabset-12-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-12-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-12-2" aria-controls="tabset-12-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-12-1" class="tab-pane active" aria-labelledby="tabset-12-1-tab">
<div class="sourceCode" id="cb60" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb60-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb60-2">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb60-3">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb60-4">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Your system prompt"</span>,</span>
<span id="cb60-5">)</span>
<span id="cb60-6"></span>
<span id="cb60-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb60-8">chat.register_tool(calculate_slide_metric)</span>
<span id="cb60-9"></span>
<span id="cb60-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb60-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb60-12">chat.chat(</span>
<span id="cb60-13">    interpolate(</span>
<span id="cb60-14">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: ..."</span></span>
<span id="cb60-15">    )</span>
<span id="cb60-16">)</span>
<span id="cb60-17"></span>
<span id="cb60-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb60-19">chat.chat_structured(</span>
<span id="cb60-20">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb60-21">    data_model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>DeckAnalysis,</span>
<span id="cb60-22">)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show complete code</summary>
<div class="sourceCode cell-code" id="cb61" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb61-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic, interpolate_file, interpolate</span>
<span id="cb61-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> subprocess</span>
<span id="cb61-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb61-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pydantic <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> BaseModel, Field</span>
<span id="cb61-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> typing <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Annotated, Optional, Union</span>
<span id="cb61-6"></span>
<span id="cb61-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown + HTML</span></span>
<span id="cb61-8">subprocess.run(</span>
<span id="cb61-9">    [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown,html"</span>]</span>
<span id="cb61-10">)</span>
<span id="cb61-11"></span>
<span id="cb61-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb61-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb61-14">audience_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb61-15">length_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb61-16">type_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb61-17">event_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb61-18"></span>
<span id="cb61-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb61-20">markdown_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span>)</span>
<span id="cb61-21">markdown_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> markdown_file.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb61-22"></span>
<span id="cb61-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb61-24">system_prompt_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-structured-tool.md"</span>)</span>
<span id="cb61-25"></span>
<span id="cb61-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb61-27">system_prompt <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb61-28">    system_prompt_file,</span>
<span id="cb61-29">    variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb61-30">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: audience_content,</span>
<span id="cb61-31">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: length_content,</span>
<span id="cb61-32">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: type_content,</span>
<span id="cb61-33">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: event_content,</span>
<span id="cb61-34">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown_content"</span>: markdown_content,</span>
<span id="cb61-35">    },</span>
<span id="cb61-36">)</span>
<span id="cb61-37"></span>
<span id="cb61-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define data structure to extract from the input</span></span>
<span id="cb61-39">ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)]</span>
<span id="cb61-40">PercentType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100.0</span>)]</span>
<span id="cb61-41">MinutesType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb61-42">SlideCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb61-43"></span>
<span id="cb61-44"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> ScoringCategory(BaseModel):</span>
<span id="cb61-45">    score: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1–10."</span>)</span>
<span id="cb61-46">    justification: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span>)</span>
<span id="cb61-47">    improvements: Optional[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-48">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>,</span>
<span id="cb61-49">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb61-50">    )</span>
<span id="cb61-51">    score_after_improvements: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-52">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb61-53">    )</span>
<span id="cb61-54"></span>
<span id="cb61-55"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> DeckAnalysis(BaseModel):</span>
<span id="cb61-56">    presentation_title: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>)</span>
<span id="cb61-57">    total_slides: SlideCount</span>
<span id="cb61-58">    percent_with_code: PercentType</span>
<span id="cb61-59">    percent_with_images: PercentType</span>
<span id="cb61-60">    estimated_duration_minutes: MinutesType</span>
<span id="cb61-61">    tone: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-62">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the tone of the presentation."</span></span>
<span id="cb61-63">    )</span>
<span id="cb61-64"></span>
<span id="cb61-65">    clarity: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-66">        ...,</span>
<span id="cb61-67">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb61-68">    )</span>
<span id="cb61-69">    relevance: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-70">        ...,</span>
<span id="cb61-71">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb61-72">    )</span>
<span id="cb61-73">    visual_design: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-74">        ...,</span>
<span id="cb61-75">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb61-76">    )</span>
<span id="cb61-77">    engagement: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-78">        ...,</span>
<span id="cb61-79">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb61-80">    )</span>
<span id="cb61-81">    pacing: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-82">        ...,</span>
<span id="cb61-83">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb61-84">    )</span>
<span id="cb61-85">    structure: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-86">        ...,</span>
<span id="cb61-87">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb61-88">    )</span>
<span id="cb61-89">    concistency: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># spelling kept as-is</span></span>
<span id="cb61-90">        ...,</span>
<span id="cb61-91">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb61-92">    )</span>
<span id="cb61-93">    accessibility: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb61-94">        ...,</span>
<span id="cb61-95">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb61-96">    )</span>
<span id="cb61-97"></span>
<span id="cb61-98"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define a tool to calculate some metrics</span></span>
<span id="cb61-99"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start with a function:</span></span>
<span id="cb61-100"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> calculate_slide_metric(metric: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> Union[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>]:</span>
<span id="cb61-101">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb61-102"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb61-103"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb61-104"></span>
<span id="cb61-105"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Parameters</span></span>
<span id="cb61-106"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    ----------</span></span>
<span id="cb61-107"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    metric : str</span></span>
<span id="cb61-108"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb61-109"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        "code" for percentage of slides containing fenced code blocks,</span></span>
<span id="cb61-110"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        or "images" for percentage of slides containing images.</span></span>
<span id="cb61-111"></span>
<span id="cb61-112"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Returns</span></span>
<span id="cb61-113"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    -------</span></span>
<span id="cb61-114"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    float or int</span></span>
<span id="cb61-115"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The calculated metric value.</span></span>
<span id="cb61-116"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb61-117">    html_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.html"</span>)</span>
<span id="cb61-118">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> html_file.exists():</span>
<span id="cb61-119">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">FileNotFoundError</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"HTML file </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>html_file<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> does not exist."</span>)</span>
<span id="cb61-120"></span>
<span id="cb61-121">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb61-122">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(html_file, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"r"</span>, encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> f:</span>
<span id="cb61-123">        html_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> f.read()</span>
<span id="cb61-124"></span>
<span id="cb61-125">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb61-126">    slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> html_content.split(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>)</span>
<span id="cb61-127">    total_slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(slides)</span>
<span id="cb61-128"></span>
<span id="cb61-129">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>:</span>
<span id="cb61-130">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> total_slides</span>
<span id="cb61-131">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>:</span>
<span id="cb61-132">        slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb61-133">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb61-134">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>:</span>
<span id="cb61-135">        slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;img"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb61-136">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb61-137">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb61-138">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">ValueError</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb61-139"></span>
<span id="cb61-140">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> result</span>
<span id="cb61-141"></span>
<span id="cb61-142"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb61-143">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb61-144">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb61-145">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>system_prompt,</span>
<span id="cb61-146">)</span>
<span id="cb61-147"></span>
<span id="cb61-148"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb61-149">chat.register_tool(calculate_slide_metric)</span>
<span id="cb61-150"></span>
<span id="cb61-151"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb61-152"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb61-153">chat.chat(</span>
<span id="cb61-154">    interpolate(</span>
<span id="cb61-155">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> markdown_content </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb61-156">    )</span>
<span id="cb61-157">)</span>
<span id="cb61-158"></span>
<span id="cb61-159"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb61-160">chat.chat_structured(</span>
<span id="cb61-161">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb61-162">    data_model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>DeckAnalysis,</span>
<span id="cb61-163">)</span></code></pre></div>
</details>
</div>
<p>We can clearly see that our tool was being called 3 times for our 3 metrics, just like it should:</p>
<div class="sourceCode" id="cb62" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb62-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 🔧 tool request (toolu_01JSsxrB8Jj9yDY35pg1g1bc)                      </span></span>
<span id="cb62-2"> calculate_slide_metric(metric<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>total_slides)                                                    </span>
<span id="cb62-3"></span>
<span id="cb62-4"></span>
<span id="cb62-5"></span>
<span id="cb62-6"> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ✅ tool result (toolu_01JSsxrB8Jj9yDY35pg1g1bc)                        </span></span>
<span id="cb62-7"> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>                                                                      </span>
<span id="cb62-8"></span>
<span id="cb62-9"></span>
<span id="cb62-10"></span>
<span id="cb62-11"> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 🔧 tool request (toolu_019635a7mRAotqTFNx2TfCsm)                      </span></span>
<span id="cb62-12"> calculate_slide_metric(metric<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>code)                                                            </span>
<span id="cb62-13"></span>
<span id="cb62-14"></span>
<span id="cb62-15"></span>
<span id="cb62-16"> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ✅ tool result (toolu_019635a7mRAotqTFNx2TfCsm)                        </span></span>
<span id="cb62-17"> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">37.5</span>                                                                    </span>
<span id="cb62-18"></span>
<span id="cb62-19"></span>
<span id="cb62-20"></span>
<span id="cb62-21"> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 🔧 tool request (toolu_01Kv6nPiUeZmABADZFeN8Qgi)                      </span></span>
<span id="cb62-22"> calculate_slide_metric(metric<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>images)                                                          </span>
<span id="cb62-23"></span>
<span id="cb62-24"></span>
<span id="cb62-25"></span>
<span id="cb62-26"> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ✅ tool result (toolu_01Kv6nPiUeZmABADZFeN8Qgi)                       </span></span>
<span id="cb62-27"> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6.25</span>                                                                    </span>
<span id="cb62-28"></span>
<span id="cb62-29"></span>
<span id="cb62-30"></span>
<span id="cb62-31"> {                                                                       </span>
<span id="cb62-32">   <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>,                                                   </span>
<span id="cb62-33">   <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"percentage_slides_with_code"</span>: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">37.5</span>,                                  </span>
<span id="cb62-34">   <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"percentage_slides_with_images"</span>: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6.25</span>                                 </span>
<span id="cb62-35"> }</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show the rest of the output</summary>
<div class="sourceCode cell-code" id="cb63" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb63-1">{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'presentation_title'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'The Shiny Side of LLMs'</span>,</span>
<span id="cb63-2"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'total_slides'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>,</span>
<span id="cb63-3"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'percent_with_code'</span>: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">37.5</span>,</span>
<span id="cb63-4"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'percent_with_images'</span>: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6.25</span>,</span>
<span id="cb63-5"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'estimated_duration_minutes'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>,</span>
<span id="cb63-6"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tone'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Conversational and friendly with technical depth, using emojis and casual language to make complex topics approachable'</span>,</span>
<span id="cb63-7"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'clarity'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,</span>
<span id="cb63-8">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Clear explanations with step-by-step progression from basics to implementation. Code examples are well-structured and concepts are explained in accessible terms.'</span>,</span>
<span id="cb63-9">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Add brief explanations of what chatlas/ellmer are on slides 5-6. Define 'reactive programming' on slide 7 for non-technical audience members."</span>,</span>
<span id="cb63-10">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>},</span>
<span id="cb63-11"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'relevance'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>,</span>
<span id="cb63-12">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Highly relevant to the posit::conf audience of Python/R users interested in AI. Content matches the technical level and practical focus expected.'</span>,</span>
<span id="cb63-13">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Consider adding a brief mention of when NOT to use LLMs in presentations on slide 14 to provide balanced perspective.'</span>,</span>
<span id="cb63-14">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>},</span>
<span id="cb63-15"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'visual_design'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>,</span>
<span id="cb63-16">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Slides are text-heavy with large code blocks. Only one visual element (GIF on slide 11). Layout is clean but could benefit from more visual variety.'</span>,</span>
<span id="cb63-17">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Add diagrams showing app architecture on slides 4 or 8. Break up dense text on slide 14 into bullet points. Consider visual examples of the DeckCheck app interface.'</span>,</span>
<span id="cb63-18">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb63-19"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'engagement'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,</span>
<span id="cb63-20">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Good use of emojis, casual tone, and interactive elements. The GIF adds humor and the practical demo concept is engaging.'</span>,</span>
<span id="cb63-21">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Add a live demo screenshot or mockup of the DeckCheck app on slide 3. Include audience interaction prompts on slides 2-3 asking about their presentation challenges.'</span>,</span>
<span id="cb63-22">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb63-23"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pacing'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,</span>
<span id="cb63-24">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Generally well-paced but some slides are dense (slides 8-9, 12-13) while others are light (slide 11). Code-heavy slides may take longer to explain.'</span>,</span>
<span id="cb63-25">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Split slide 14 into two slides - one for UX principles, one for error handling. Reduce code on slides 8-9 to focus on key concepts only.'</span>,</span>
<span id="cb63-26">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>},</span>
<span id="cb63-27"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'structure'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,</span>
<span id="cb63-28">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Clear logical flow from introduction to requirements to implementation to next steps. Good narrative arc building toward a complete solution.'</span>,</span>
<span id="cb63-29">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Add a brief agenda/outline slide after slide 1 to preview the journey. Include a summary slide before 'Thank you' recapping key takeaways."</span>,</span>
<span id="cb63-30">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>},</span>
<span id="cb63-31"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'concistency'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,</span>
<span id="cb63-32">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Consistent formatting, tone, and code style throughout. Good parallel structure between Python and R examples.'</span>,</span>
<span id="cb63-33">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Ensure consistent capitalization in slide titles (slide 11 uses title case while others use sentence case). Standardize code comment styles across examples.'</span>,</span>
<span id="cb63-34">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>},</span>
<span id="cb63-35"> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'accessibility'</span>: {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,</span>
<span id="cb63-36">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'justification'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Text appears readable but code blocks are quite dense. Good contrast likely but some slides have significant cognitive load.'</span>,</span>
<span id="cb63-37">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'improvements'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Increase font size in code blocks on slides 5-6, 8-9, 12-13. Add alt text description for the GIF on slide 11. Use consistent heading hierarchy.'</span>,</span>
<span id="cb63-38">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'score_after_improvements'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>}}</span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-12-2" class="tab-pane" aria-labelledby="tabset-12-2-tab">
<div class="sourceCode" id="cb64" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb64-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb64-2">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb64-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb64-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt</span>
<span id="cb64-5">)</span>
<span id="cb64-6"></span>
<span id="cb64-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb64-8">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(calculate_slide_metric)</span>
<span id="cb64-9"></span>
<span id="cb64-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb64-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb64-12">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(</span>
<span id="cb64-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb64-14">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb64-15">  )</span>
<span id="cb64-16">)</span>
<span id="cb64-17"></span>
<span id="cb64-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb64-19">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_structured</span>(</span>
<span id="cb64-20">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb64-21">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_deck_analysis</span>
<span id="cb64-22">)</span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show complete code</summary>
<div class="sourceCode cell-code" id="cb65" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb65-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb65-2"></span>
<span id="cb65-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown + HTML</span></span>
<span id="cb65-4">quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb65-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>,</span>
<span id="cb65-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"html"</span>)</span>
<span id="cb65-7">)</span>
<span id="cb65-8"></span>
<span id="cb65-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb65-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb65-11">audience_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb65-12">length_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb65-13">type_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb65-14">event_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb65-15"></span>
<span id="cb65-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb65-17">markdown_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span></span>
<span id="cb65-18">markdown_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(markdown_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(markdown_file))</span>
<span id="cb65-19"></span>
<span id="cb65-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb65-21">system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-structured-tool.md"</span></span>
<span id="cb65-22"></span>
<span id="cb65-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb65-24">system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb65-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb65-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> audience_content,</span>
<span id="cb65-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> length_content,</span>
<span id="cb65-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_content,</span>
<span id="cb65-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> event_content</span>
<span id="cb65-30">)</span>
<span id="cb65-31"></span>
<span id="cb65-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reusable scoring category</span></span>
<span id="cb65-33">type_scoring_category <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb65-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb65-35">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1 to 10."</span></span>
<span id="cb65-36">  ),</span>
<span id="cb65-37">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">justification =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb65-38">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span></span>
<span id="cb65-39">  ),</span>
<span id="cb65-40">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb65-41">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb65-42">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb65-43">  ),</span>
<span id="cb65-44">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score_after_improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb65-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb65-46">  )</span>
<span id="cb65-47">)</span>
<span id="cb65-48"></span>
<span id="cb65-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Top-level deck analysis object</span></span>
<span id="cb65-50">type_deck_analysis <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb65-51">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">presentation_title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>),</span>
<span id="cb65-52">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_slides =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total number of slides."</span>),</span>
<span id="cb65-53">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb65-54">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing code blocks (0–100)."</span></span>
<span id="cb65-55">  ),</span>
<span id="cb65-56">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_images =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb65-57">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing images (0–100)."</span></span>
<span id="cb65-58">  ),</span>
<span id="cb65-59">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">estimated_duration_minutes =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb65-60">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated presentation length in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide."</span></span>
<span id="cb65-61">  ),</span>
<span id="cb65-62">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tone =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb65-63">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the presentation tone (e.g., informal, technical, playful)."</span></span>
<span id="cb65-64">  ),</span>
<span id="cb65-65">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">clarity =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-66">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb65-67">    type_scoring_category</span>
<span id="cb65-68">  ),</span>
<span id="cb65-69">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">relevance =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-70">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Asses how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb65-71">    type_scoring_category</span>
<span id="cb65-72">  ),</span>
<span id="cb65-73">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">visual_design =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-74">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb65-75">    type_scoring_category</span>
<span id="cb65-76">  ),</span>
<span id="cb65-77">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">engagement =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-78">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb65-79">    type_scoring_category</span>
<span id="cb65-80">  ),</span>
<span id="cb65-81">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pacing =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-82">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb65-83">    type_scoring_category</span>
<span id="cb65-84">  ),</span>
<span id="cb65-85">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">structure =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-86">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb65-87">    type_scoring_category</span>
<span id="cb65-88">  ),</span>
<span id="cb65-89">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">concistency =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-90">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb65-91">    type_scoring_category</span>
<span id="cb65-92">  ),</span>
<span id="cb65-93">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accessibility =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb65-94">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb65-95">    type_scoring_category</span>
<span id="cb65-96">  )</span>
<span id="cb65-97">)</span>
<span id="cb65-98"></span>
<span id="cb65-99"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define a tool to calculate some metrics</span></span>
<span id="cb65-100"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start with a function:</span></span>
<span id="cb65-101"></span>
<span id="cb65-102"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb65-103"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb65-104"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb65-105"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param metric The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb65-106"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' "code" for percentage of slides containing fenced code blocks, or "images"</span></span>
<span id="cb65-107"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' for percentage of slides containing images.</span></span>
<span id="cb65-108"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return The calculated metric value.</span></span>
<span id="cb65-109">calculate_slide_metric <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(metric) {</span>
<span id="cb65-110">  html_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.html"</span></span>
<span id="cb65-111">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.exists</span>(html_file)) {</span>
<span id="cb65-112">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(</span>
<span id="cb65-113">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"HTML file does not exist. Please render your Quarto presentation first."</span></span>
<span id="cb65-114">    )</span>
<span id="cb65-115">  }</span>
<span id="cb65-116">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb65-117">  html_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(html_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(html_file))</span>
<span id="cb65-118"></span>
<span id="cb65-119">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb65-120">  slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strsplit</span>(html_content, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>))</span>
<span id="cb65-121"></span>
<span id="cb65-122">  total_slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(slides)</span>
<span id="cb65-123"></span>
<span id="cb65-124">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>) {</span>
<span id="cb65-125">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> total_slides</span>
<span id="cb65-126">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>) {</span>
<span id="cb65-127">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides where we see the "sourceCode" class</span></span>
<span id="cb65-128">    slides_with_code <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span>, slides))</span>
<span id="cb65-129">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb65-130">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>) {</span>
<span id="cb65-131">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides with image tag</span></span>
<span id="cb65-132">    slides_with_image <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'&lt;img'</span>, slides))</span>
<span id="cb65-133">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb65-134">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> {</span>
<span id="cb65-135">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb65-136">  }</span>
<span id="cb65-137"></span>
<span id="cb65-138">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(result)</span>
<span id="cb65-139">}</span>
<span id="cb65-140"></span>
<span id="cb65-141"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Optionally, to avoid manual work:</span></span>
<span id="cb65-142"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># create_tool_def(calculate_slide_metric)</span></span>
<span id="cb65-143">calculate_slide_metric <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb65-144">  calculate_slide_metric,</span>
<span id="cb65-145">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Returns the calculated metric value"</span>,</span>
<span id="cb65-146">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">metric =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb65-147">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'The metric to calculate: "total_slides" for total number of slides, </span></span>
<span id="cb65-148"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      "code" for percentage of slides containing fenced code blocks, or "images"</span></span>
<span id="cb65-149"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      for percentage of slides containing images.'</span>,</span>
<span id="cb65-150">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb65-151">  )</span>
<span id="cb65-152">)</span>
<span id="cb65-153"></span>
<span id="cb65-154"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb65-155">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb65-156">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb65-157">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt</span>
<span id="cb65-158">)</span>
<span id="cb65-159"></span>
<span id="cb65-160"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb65-161">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(calculate_slide_metric)</span>
<span id="cb65-162"></span>
<span id="cb65-163"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb65-164"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb65-165">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(</span>
<span id="cb65-166">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb65-167">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb65-168">  )</span>
<span id="cb65-169">)</span>
<span id="cb65-170"></span>
<span id="cb65-171"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb65-172">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_structured</span>(</span>
<span id="cb65-173">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb65-174">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_deck_analysis</span>
<span id="cb65-175">)</span></code></pre></div>
</details>
</div>
<p>We can clearly see that our tool was being called 3 times for our 3 metrics, just like it should:</p>
<div class="sourceCode" id="cb66" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb66-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ◯ [tool call] calculate_slide_metric(metric = "total_slides")</span></span>
<span id="cb66-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ● #&gt; 16</span></span>
<span id="cb66-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ◯ [tool call] calculate_slide_metric(metric = "code")</span></span>
<span id="cb66-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ● #&gt; 37.5</span></span>
<span id="cb66-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ◯ [tool call] calculate_slide_metrimetric = "images")</span></span>
<span id="cb66-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ● #&gt; 6.25</span></span>
<span id="cb66-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ```json</span></span>
<span id="cb66-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; {</span></span>
<span id="cb66-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "total_slides": 16,</span></span>
<span id="cb66-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percentage_slides_with_code": 37.5,</span></span>
<span id="cb66-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percentage_slides_with_images": 6.25</span></span>
<span id="cb66-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; }</span></span>
<span id="cb66-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ```</span></span></code></pre></div>
<div class="cell">
<details class="code-fold">
<summary>Show the rest of the output</summary>
<div class="sourceCode cell-code" id="cb67" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb67-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ◯ [tool call] calculate_slide_metric(metric = "total_slides")</span></span>
<span id="cb67-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ● #&gt; 16</span></span>
<span id="cb67-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ◯ [tool call] calculate_slide_metric(metric = "code")</span></span>
<span id="cb67-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ● #&gt; 37.5</span></span>
<span id="cb67-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ◯ [tool call] calculate_slide_metric(metric = "images")</span></span>
<span id="cb67-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ● #&gt; 6.25</span></span>
<span id="cb67-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ```json</span></span>
<span id="cb67-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; {</span></span>
<span id="cb67-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "total_slides": 16,</span></span>
<span id="cb67-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percentage_slides_with_code": 37.5,</span></span>
<span id="cb67-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   "percentage_slides_with_images": 6.25</span></span>
<span id="cb67-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; }</span></span>
<span id="cb67-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ```</span></span>
<span id="cb67-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $presentation_title</span></span>
<span id="cb67-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "The Shiny Side of LLMs"</span></span>
<span id="cb67-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $total_slides</span></span>
<span id="cb67-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 16</span></span>
<span id="cb67-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $percent_with_code</span></span>
<span id="cb67-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 37.5</span></span>
<span id="cb67-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $percent_with_images</span></span>
<span id="cb67-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 6.25</span></span>
<span id="cb67-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $estimated_duration_minutes</span></span>
<span id="cb67-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] 12</span></span>
<span id="cb67-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $tone</span></span>
<span id="cb67-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "Informal and conversational with technical depth"</span></span>
<span id="cb67-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $clarity</span></span>
<span id="cb67-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     8</span></span>
<span id="cb67-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                  justification</span></span>
<span id="cb67-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Content is well-explained with clear examples and step-by-step progression. Technical concepts are introduced appropriately.</span></span>
<span id="cb67-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                                                      improvements</span></span>
<span id="cb67-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add brief definition of LLMs on slide 2 for audience members new to the topic. Consider adding expected output examples on slides 5-6 to show what the LLM responses look like.</span></span>
<span id="cb67-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        9</span></span>
<span id="cb67-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-42"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $relevance</span></span>
<span id="cb67-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     9</span></span>
<span id="cb67-45"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                                justification</span></span>
<span id="cb67-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Highly relevant for the posit::conf audience - focuses on practical implementation in both R and Python with Shiny, which is core to the conference theme.</span></span>
<span id="cb67-47"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                             improvements</span></span>
<span id="cb67-48"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Content is already well-targeted for the audience. No significant improvements needed.</span></span>
<span id="cb67-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        9</span></span>
<span id="cb67-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-52"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $visual_design</span></span>
<span id="cb67-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     6</span></span>
<span id="cb67-55"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                              justification</span></span>
<span id="cb67-56"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Layout is clean but quite text-heavy. Code blocks are well-formatted, but some slides like slide 8 and 14 contain large amounts of text.</span></span>
<span id="cb67-57"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                                                                                                            improvements</span></span>
<span id="cb67-58"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Break up slide 8 (Shiny basics: Python) into 2 slides - separate UI and server logic. Split slide 14 (User experience) into bullet points or multiple slides. Add more visual elements like diagrams or screenshots of the final app.</span></span>
<span id="cb67-59"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-60"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span>
<span id="cb67-61"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-62"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $engagement</span></span>
<span id="cb67-63"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     7</span></span>
<span id="cb67-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                           justification</span></span>
<span id="cb67-66"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Good use of emojis and conversational tone. The magic GIF on slide 10 adds personality. However, mostly text-based with limited interactive elements.</span></span>
<span id="cb67-67"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                                                     improvements</span></span>
<span id="cb67-68"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add a live demo screenshot or video on slide 15 showing the final DeckCheck app in action. Consider adding a simple architecture diagram on slide 4 to visualize the workflow.</span></span>
<span id="cb67-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span>
<span id="cb67-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $pacing</span></span>
<span id="cb67-73"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-74"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     7</span></span>
<span id="cb67-75"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                        justification</span></span>
<span id="cb67-76"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Good balance overall, but some slides are content-heavy (slides 8, 9, 12, 13) which may feel rushed in a 10-minute lightning talk.</span></span>
<span id="cb67-77"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                                                                           improvements</span></span>
<span id="cb67-78"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Simplify code examples on slides 8-9 by showing only key parts. Break slide 12-13 into smaller, focused slides. Consider removing some implementation details to focus on the core concept and demo.</span></span>
<span id="cb67-79"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-80"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span>
<span id="cb67-81"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-82"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $structure</span></span>
<span id="cb67-83"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-84"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     8</span></span>
<span id="cb67-85"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                         justification</span></span>
<span id="cb67-86"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Clear logical flow from introduction to implementation to next steps. Good progression from basic concepts to complete integration.</span></span>
<span id="cb67-87"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                                          improvements</span></span>
<span id="cb67-88"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Add a brief agenda or roadmap slide after slide 1 to set expectations. Include a quick recap slide before the 'Up next' section to summarize what was accomplished.</span></span>
<span id="cb67-89"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-90"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        9</span></span>
<span id="cb67-91"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-92"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $concistency</span></span>
<span id="cb67-93"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-94"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     8</span></span>
<span id="cb67-95"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                          justification</span></span>
<span id="cb67-96"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Consistent formatting for code blocks and good parallel structure between R and Python examples. Tone remains consistent throughout.</span></span>
<span id="cb67-97"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                             improvements</span></span>
<span id="cb67-98"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Ensure slide titles follow consistent capitalization (slide 10 'Where's the magic?' vs others). Standardize bullet point formatting across all slides.</span></span>
<span id="cb67-99"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-100"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        9</span></span>
<span id="cb67-101"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb67-102"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $accessibility</span></span>
<span id="cb67-103"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score</span></span>
<span id="cb67-104"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     7</span></span>
<span id="cb67-105"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                justification</span></span>
<span id="cb67-106"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Code is readable with good contrast. However, some slides have dense text that may be challenging to read quickly during a lightning talk.</span></span>
<span id="cb67-107"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;                                                                                                                                                 improvements</span></span>
<span id="cb67-108"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 Increase font size for code blocks on slides 5-6, 8-9, 12-13. Reduce text density on slide 14. Ensure the GIF on slide 10 has alt text for screen readers.</span></span>
<span id="cb67-109"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   score_after_improvements</span></span>
<span id="cb67-110"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1                        8</span></span></code></pre></div>
</details>
</div>
</div>
</div>
</div>
<p>So how does the LLM know that it needs to use a tool? We didn’t specifically tell it to use it. We just registered it and let the LLM figure it out for itself. How does that work? The first step in the process is turning a vague, human-friendly request into something a tool can actually run. In our case, our prompt contained this:</p>
<div class="sourceCode" id="cb68" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb68-1">...</span>
<span id="cb68-2"></span>
<span id="cb68-3">You extract the following information:</span>
<span id="cb68-4"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>The number of slides</span>
<span id="cb68-5"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>The percentage of slides containing code blocks</span>
<span id="cb68-6"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>The percentage of slides containing images </span>
<span id="cb68-7"></span>
<span id="cb68-8">Return only the JSON results, nothing else.</span>
<span id="cb68-9"></span>
<span id="cb68-10">...</span></code></pre></div>
<p>When the LLM sees “The percentage of slides containing images,” it doesn’t just guess. It translates that phrase into structured parameters for the registered tool: <code>calculate_slide_metric</code>. The LLM knows it needs to set <code>metric</code> to <code>"images"</code> or <code>"code"</code>, depending on the request. Once it has those details, it calls the tool with this parameter and responds with the result in JSON (and nothing else, as we told it to: LLMs love to talk).</p>
<p>This is where tool calling really shines. It’s not just about running code: it’s about structuring messy, open-ended requests into something that can be understood by our code. You could ask “Show me how code-heavy my slides are,” “Can you tell me how many images are in this presentation?” or “What percentage of slides have images?” and the LLM would know to call the same tool. The wording changes, but the intent is the same, and the LLM can map all those variations to the right parameters. It’s pretty clever!</p>
<p>The parameters make it even more powerful. If we created a tool with no parameters (e.g.&nbsp;one that only ever returned the percentage of image slides), it could only answer that exact question. But by designing it with parameters, it’s way more flexible. The same <code>calculate_slide_metric</code> function can handle requests for total slides, code slides, image slides, or anything else we choose to add later. And because the LLM can chain tool calls, aka run the tool more than once in a conversation, it can check for the 3 metrics all at once and bundle the results together in one tidy JSON.</p>
</section>
<section id="from-tools-to-agents" class="level1">
<h1>From tools to agents</h1>
<p>Right now, DeckCheck’s tool does one (or, ok, technically speaking, three) things: it calculates the metrics you’ve defined. That’s it. But tool calling can go much further. Imagine that, instead of passing parameters to code you’ve already written, the LLM could actually write its own code to analyse your slides. Creepy, right? The LLM is just… doing its own thing. This is sometimes called ”agentic AI”, where the LLM can formulate and execute coding tasks to learn about <em>and alter</em> the state of the world.</p>
<p>Sounds cool, but what if the LLM decides to do something that’s harmful? Imagine it dropping database tables, overwriting files, or sending data somewhere it shouldn’t. Without limits, arbitrary code execution can be as risky as it is powerful. That’s why you need to take some safety measures. For example, if you let the LLM write SQL statements, you can limit it so it only allows read-only SQL executing. This is exactly what <a href="https://github.com/posit-dev/querychat">querychat</a> does. Or, instead of giving the LLM complete freedom, you can add a human back into the loop by asking explicit user approval before anything gets executed. An example of such a project is the new (experimental) agent called <a href="https://open-vsx.org/extension/posit/databot">Databot</a>. Databot is there to assist you in your data analysis, making you faster and more efficient. But it doesn’t mean it can replace you: to use these tools effectively and safely, <a href="https://posit.co/blog/databot-is-not-a-flotation-device/">your human skills are still needed</a>. Curious to learn more about Databot? Check out the <a href="https://posit.co/blog/introducing-databot/">official announcement</a>!</p>
<p>This is also where MCP (<a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol</a>) comes in. You have data or tools (an R/Python function, an API, a database, spreadsheet, whatever). You want an LLM to use them. But the LLM doesn’t know what you have or how to access it. The problem is, the LLM doesn’t automatically know what you have or how to use it. MCP solves that by providing a standard way to describe these tools and make them available to the model. It’s like handing the LLM a clearly written “menu” of what’s on offer and how to order it. That’s why MCP is often described as “a USB-C port for LLM applications”: it standardises how models can plug into all sorts of tools and data sources. It opens a world of possibilities. But the details of MCP are a whole topic on their own, so we’ll leave that for another time. If you got curious about MCP, you can check out the relevant section in the <a href="https://posit-dev.github.io/chatlas/misc/mcp-tools.html"><code>chatlas</code> documentation</a>, or <a href="https://posit-dev.github.io/mcptools/"><code>mcptools</code></a> for some R fun with MCP.</p>
</section>
<section id="model-parameters" class="level1">
<h1>Model parameters</h1>
<p>We covered the “big” parts to get a response we can work with: we set a system prompt, crafted a nice detailed input prompt, used structured output, and gave the LLM some extra calculation power with a tool. But still we are getting slightly different results every single time: the suggested improvements are always different and even things like estimated duration vary. Ideally, we want to have as much “control” as possible over the response an LLM generates. And while we can’t 100% control what an LLM does, we can tweak its behaviour by changing (some of) the settings. These settings are also called model parameters.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
A bit playing around the edges
</div>
</div>
<div class="callout-body-container callout-body">
<p>Tweaking model parameters is playing around the edges: it won’t magically fix a bad prompt. Writing a good prompt and knowing how to use <code>chatlas</code> or <code>ellmer</code> programmatically already give a solid foundation. Normally default settings for model parameters are the default for a reason: they tend to give the most desired results. But understanding which parameters there are and what kind of effect they have on the LLM’s response is a good exercise anyway!</p>
</div>
</div>
<p>There are many model parameters, and the ones you can change are different for each provider and model, but generally, these are the most noteworthy ones:</p>
<ul>
<li><p><strong>Temperature</strong>: when you ask an LLM the same question you might expect the same answer, just like a calculator always gives the same result for 2 + 2. But an LLM isn’t a calculator: it’s more like a smart writer. It ”knows” how it should answer your question, but it can phrase it in many ways. That’s because its default settings allow a little randomness, a little “creativity”. The wording, order, or formatting might change, even if the meaning stays the same. This creativity is also called the model’s temperature. Typically, the temperature ranges from 0.0 to 1.0. A temperature closer to 1.0 is great for creative and generative tasks, and a temperature closer to 0.0 is best for analytical tasks. Note that a temperature of 0 doesn’t mean an LLM will be fully deterministic: probability mechanisms will always cause slight variations in answers. Also note that a temperature of 1 might lead to very creative answers that don’t make much sense anymore.</p></li>
<li><p><strong>Seed</strong>: adds a bit of predictability to a model that’s normally unpredictable. As mentioned before, asking the same question twice (especially with a higher temperature) gives you slightly different answers. That’s great for creativity, but not always what you want. By setting a seed you make randomness reproducible. It tells the model to “start from the same random point” every time. This is especially helpful for testing or generating consistent results in production. Note: not all LLM providers support the seed parameter! Looking at the <a href="https://docs.anthropic.com/en/api/messages">documentation</a>, Anthropic does not seem to support this parameter in API calls.</p></li>
<li><p><strong>Top-p</strong>: controls how many possible words the model can choose from when generating a response. It does this by adding up the most likely words until a probability threshold is reached. Let’s take our original prompt as an example: “I’m working on a presentation with the title: ‘The Shiny Side of LLMs’. What’s your feedback just based on that title?”. The model considers several next phrases:</p>
<ul>
<li>“is catchy and clear” (50%)</li>
<li>“could be more specific” (25%)</li>
<li>“is creative but vague” (15%)</li>
<li>“needs glitter” (7%)</li>
<li>“mentions Dalmatians” (3%)</li>
</ul>
<p>If top-p = 0.9, only the top three are used (since 50% + 25% + 15% = 90%). The model randomly picks one of those three options, with the probability of each token still guiding the choice (so the 50% token is much more likely to be picked than the 15% one). If top-p = 0.75, the model only considers the first two options. A higher top-p value leads to more variety, while a lower top-p leads to more focussed and “safe” answers.</p>
<p>It’s not recommended to control creativity via both temperature or top-p: pick one. If you start tweaking both parameters they might cancel each other out and it can lead to a weird balance. Changing the temperature is the simpler and more predictive way to control creativity: it’s more linear and easier to wrap your head around.</p></li>
<li><p><strong>Maximum length or maximum output tokens</strong>: the limit on how long the model’s response can be. Different models have different maximum values for this parameter. Note that with tokens, you’re not counting characters or words. Instead, you’re telling the model: “Don’t generate more than X chunks of language”. For example, Claude Sonnet 4 has a <a href="https://docs.anthropic.com/en/docs/about-claude/models/overview#model-comparison-table">maximum output of 64,000 tokens</a> (which is around 48,000 words).</p></li>
</ul>
<p>And to come back to some jargon, changing any of these model parameters is called inference tweaking. It’s trying to control how creative, long, or focused responses are.</p>
<p>Model parameters are supported by most providers and with <code>chatlas</code> and <code>ellmer</code> you can quickly set them in a provider-agnostic way.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-13-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-13-1" aria-controls="tabset-13-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-13-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-13-2" aria-controls="tabset-13-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-13-1" class="tab-pane active" aria-labelledby="tabset-13-1-tab">
<div class="sourceCode" id="cb69" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb69-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set model parameters (optional)</span></span>
<span id="cb69-2">chat.set_model_params(</span>
<span id="cb69-3">    temperature<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>, <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb69-4">)</span></code></pre></div>
<p>By using the <a href="https://posit-dev.github.io/chatlas/reference/Chat.html#set_model_params.qmd"><code>set_model_params()</code></a> method on the chat object, you can quickly set things like <code>temperature</code>, <code>max_tokens</code> or <code>top_p</code>. These model parameters apply to the whole chat. If you want to revert to the default parameters, you can pass <code>None</code> to the relevant parameter.</p>
</div>
<div id="tabset-13-2" class="tab-pane" aria-labelledby="tabset-13-2-tab">
<div class="sourceCode" id="cb70" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb70-1">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb70-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb70-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span>(</span>
<span id="cb70-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temperature =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb70-5">  )</span>
<span id="cb70-6">)</span></code></pre></div>
<p>Adding parameters to our model is easy: we use the <a href="https://ellmer.tidyverse.org/reference/params.html"><code>params</code> argument</a> in <code>chat_anthropic()</code>. We create those parameters with <a href="https://ellmer.tidyverse.org/reference/params.html"><code>params()</code></a>. This helper function creates a list of parameters, where parameter names are automatically standardised and included in the correctly place in the API call. You won’t have to do a thing. Thanks <code>ellmer</code>!</p>
<p>Note: since <code>ellmer</code> 0.3.0 you can also use <a href="https://ellmer.tidyverse.org/reference/chat-any.html"><code>chat()</code></a> and pass the <code>params</code> argument there.</p>
</div>
</div>
</div>
<p>Besides model parameters, which are commonly used across providers, there’s also something called <a href="https://posit-dev.github.io/chatlas/get-started/parameters.html#chat-parameters">chat parameters</a>. The difference is that the latter are specific to the model provider you’re using.</p>
</section>
<section id="full-workflow" class="level1">
<h1>Full workflow</h1>
<p>A nice prompt, structured output, tool calling, model parameters… It all led us to the following workflow:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-14-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-14-1" aria-controls="tabset-14-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-14-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-14-2" aria-controls="tabset-14-2" aria-selected="false">R</a></li><li class="nav-item"><a class="nav-link" id="tabset-14-3-tab" data-bs-toggle="tab" data-bs-target="#tabset-14-3" aria-controls="tabset-14-3" aria-selected="false">System prompt</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-14-1" class="tab-pane active" aria-labelledby="tabset-14-1-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Get this code from GitHub
</div>
</div>
<div class="callout-body-container callout-body">
<p>You can grab the code directly from <a href="https://github.com/hypebright/the-shiny-side-of-llms/blob/d1094d2774f9d0c213c7ddf6e17f94da706b1b76/Py/demo/conversation-tool.py">here</a>.</p>
</div>
</div>
<div class="cell">
<details class="code-fold">
<summary>See full workflow</summary>
<div class="sourceCode cell-code" id="cb71" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb71-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> chatlas <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ChatAnthropic, interpolate_file</span>
<span id="cb71-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> subprocess</span>
<span id="cb71-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb71-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pydantic <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> BaseModel, Field</span>
<span id="cb71-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> typing <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Annotated, Optional, Union</span>
<span id="cb71-6"></span>
<span id="cb71-7">chat <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ChatAnthropic(</span>
<span id="cb71-8">    model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb71-9">    system_prompt<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"You are a presentation coach for data scientists. You give constructive, focused, and practical feedback on titles, structure, and storytelling."</span>,</span>
<span id="cb71-10">)</span>
<span id="cb71-11"></span>
<span id="cb71-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set model parameters (optional)</span></span>
<span id="cb71-13">chat.set_model_params(</span>
<span id="cb71-14">    temperature<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>,  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb71-15">)</span>
<span id="cb71-16"></span>
<span id="cb71-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown + HTML</span></span>
<span id="cb71-18">subprocess.run(</span>
<span id="cb71-19">    [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./my-presentation.qmd"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--to"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown,html"</span>]</span>
<span id="cb71-20">)</span>
<span id="cb71-21"></span>
<span id="cb71-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Use prompt file</span></span>
<span id="cb71-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Step 1: first step of the analysis (meta-data)</span></span>
<span id="cb71-24">prompt_file_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-structured-tool-1.md"</span></span>
<span id="cb71-25"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Step 2: second step of the analysis (detailed analysis with improvements)</span></span>
<span id="cb71-26">prompt_file_2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-structured-tool-2.md"</span>)</span>
<span id="cb71-27"></span>
<span id="cb71-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb71-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb71-30">audience_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb71-31">length_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb71-32">type_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb71-33">event_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb71-34"></span>
<span id="cb71-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb71-36">markdown_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./docs/my-presentation.md"</span>)</span>
<span id="cb71-37">markdown_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> markdown_file.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb71-38"></span>
<span id="cb71-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Construct the first prompt</span></span>
<span id="cb71-40">prompt_complete_1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> interpolate_file(</span>
<span id="cb71-41">    prompt_file_1,</span>
<span id="cb71-42">    variables<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb71-43">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"audience"</span>: audience_content,</span>
<span id="cb71-44">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"length"</span>: length_content,</span>
<span id="cb71-45">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"type"</span>: type_content,</span>
<span id="cb71-46">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"event"</span>: event_content,</span>
<span id="cb71-47">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>: markdown_content,</span>
<span id="cb71-48">    },</span>
<span id="cb71-49">)</span>
<span id="cb71-50"></span>
<span id="cb71-51"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the second prompt (no dynamic data)</span></span>
<span id="cb71-52">prompt_complete_2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> prompt_file_2.read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb71-53"></span>
<span id="cb71-54"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define data structure to extract from the input</span></span>
<span id="cb71-55">ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)]</span>
<span id="cb71-56">PercentType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, le<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100.0</span>)]</span>
<span id="cb71-57">MinutesType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb71-58">SlideCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Annotated[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, Field(ge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)]</span>
<span id="cb71-59"></span>
<span id="cb71-60"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> ScoringCategory(BaseModel):</span>
<span id="cb71-61">    score: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1–10."</span>)</span>
<span id="cb71-62">    justification: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span>)</span>
<span id="cb71-63">    improvements: Optional[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-64">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>,</span>
<span id="cb71-65">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb71-66">    )</span>
<span id="cb71-67">    score_after_improvements: ScoreType <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-68">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb71-69">    )</span>
<span id="cb71-70"></span>
<span id="cb71-71"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">class</span> DeckAnalysis(BaseModel):</span>
<span id="cb71-72">    presentation_title: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>)</span>
<span id="cb71-73">    total_slides: SlideCount</span>
<span id="cb71-74">    percent_with_code: PercentType</span>
<span id="cb71-75">    percent_with_images: PercentType</span>
<span id="cb71-76">    estimated_duration_minutes: MinutesType</span>
<span id="cb71-77">    tone: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-78">        ..., description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the tone of the presentation."</span></span>
<span id="cb71-79">    )</span>
<span id="cb71-80"></span>
<span id="cb71-81">    clarity: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-82">        ...,</span>
<span id="cb71-83">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb71-84">    )</span>
<span id="cb71-85">    relevance: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-86">        ...,</span>
<span id="cb71-87">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb71-88">    )</span>
<span id="cb71-89">    visual_design: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-90">        ...,</span>
<span id="cb71-91">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb71-92">    )</span>
<span id="cb71-93">    engagement: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-94">        ...,</span>
<span id="cb71-95">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb71-96">    )</span>
<span id="cb71-97">    pacing: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-98">        ...,</span>
<span id="cb71-99">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb71-100">    )</span>
<span id="cb71-101">    structure: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-102">        ...,</span>
<span id="cb71-103">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb71-104">    )</span>
<span id="cb71-105">    concistency: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># spelling kept as-is</span></span>
<span id="cb71-106">        ...,</span>
<span id="cb71-107">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb71-108">    )</span>
<span id="cb71-109">    accessibility: ScoringCategory <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Field(</span>
<span id="cb71-110">        ...,</span>
<span id="cb71-111">        description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb71-112">    )</span>
<span id="cb71-113"></span>
<span id="cb71-114"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define a tool to calculate some metrics</span></span>
<span id="cb71-115"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start with a function:</span></span>
<span id="cb71-116"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> calculate_slide_metric(metric: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> Union[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>]:</span>
<span id="cb71-117">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb71-118"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb71-119"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb71-120"></span>
<span id="cb71-121"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Parameters</span></span>
<span id="cb71-122"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    ----------</span></span>
<span id="cb71-123"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    metric : str</span></span>
<span id="cb71-124"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb71-125"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        "code" for percentage of slides containing fenced code blocks,</span></span>
<span id="cb71-126"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        or "images" for percentage of slides containing images.</span></span>
<span id="cb71-127"></span>
<span id="cb71-128"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Returns</span></span>
<span id="cb71-129"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    -------</span></span>
<span id="cb71-130"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    float or int</span></span>
<span id="cb71-131"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">        The calculated metric value.</span></span>
<span id="cb71-132"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb71-133">    html_file <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./docs/my-presentation.html"</span>)</span>
<span id="cb71-134">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">not</span> html_file.exists():</span>
<span id="cb71-135">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">FileNotFoundError</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"HTML file </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>html_file<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> does not exist."</span>)</span>
<span id="cb71-136"></span>
<span id="cb71-137">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb71-138">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(html_file, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"r"</span>, encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> f:</span>
<span id="cb71-139">        html_content <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> f.read()</span>
<span id="cb71-140"></span>
<span id="cb71-141">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb71-142">    slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> html_content.split(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>)</span>
<span id="cb71-143">    total_slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(slides)</span>
<span id="cb71-144"></span>
<span id="cb71-145">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>:</span>
<span id="cb71-146">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> total_slides</span>
<span id="cb71-147">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>:</span>
<span id="cb71-148">        slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb71-149">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb71-150">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">elif</span> metric <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>:</span>
<span id="cb71-151">        slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;img"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slide <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">for</span> slide <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">in</span> slides)</span>
<span id="cb71-152">        result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb71-153">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span>:</span>
<span id="cb71-154">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">ValueError</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb71-155"></span>
<span id="cb71-156">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> result</span>
<span id="cb71-157"></span>
<span id="cb71-158">chat.register_tool(calculate_slide_metric)</span>
<span id="cb71-159"></span>
<span id="cb71-160"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Step 1: use regular chat to extract meta-data</span></span>
<span id="cb71-161"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Note that this *should* make use of our tool</span></span>
<span id="cb71-162">chat.chat(prompt_complete_1)</span>
<span id="cb71-163"></span>
<span id="cb71-164"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Step 2: use structured chat to further analyse the slides</span></span>
<span id="cb71-165">chat.chat_structured(prompt_complete_2, data_model<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>DeckAnalysis)</span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-14-2" class="tab-pane" aria-labelledby="tabset-14-2-tab">
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Get this code from GitHub
</div>
</div>
<div class="callout-body-container callout-body">
<p>You can grab the code directly from <a href="https://github.com/hypebright/the-shiny-side-of-llms/blob/d1094d2774f9d0c213c7ddf6e17f94da706b1b76/R/demo/conversation-tool.R">here</a>.</p>
</div>
</div>
<div class="cell">
<details class="code-fold">
<summary>See full workflow</summary>
<div class="sourceCode cell-code" id="cb72" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb72-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ellmer)</span>
<span id="cb72-2"></span>
<span id="cb72-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get Quarto presentation and convert to plain Markdown + HTML</span></span>
<span id="cb72-4">quarto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto_render</span>(</span>
<span id="cb72-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/my-presentation.qmd"</span>,</span>
<span id="cb72-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">output_format =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"markdown"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"html"</span>)</span>
<span id="cb72-7">)</span>
<span id="cb72-8"></span>
<span id="cb72-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Dynamic data</span></span>
<span id="cb72-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Audience, length in minutes, type, and event</span></span>
<span id="cb72-11">audience_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Python and R users who are curious about AI and large language models, but not all of them have a deep technical background"</span></span>
<span id="cb72-12">length_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"10"</span></span>
<span id="cb72-13">type_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lightning talk"</span></span>
<span id="cb72-14">event_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posit::conf(2025)"</span></span>
<span id="cb72-15"></span>
<span id="cb72-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read the generated Markdown file containing our slides</span></span>
<span id="cb72-17">markdown_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.md"</span></span>
<span id="cb72-18">markdown_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(markdown_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(markdown_file))</span>
<span id="cb72-19"></span>
<span id="cb72-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define prompt file</span></span>
<span id="cb72-21">system_prompt_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./prompts/prompt-analyse-slides-structured-tool.md"</span></span>
<span id="cb72-22"></span>
<span id="cb72-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create system prompt</span></span>
<span id="cb72-24">system_prompt <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate_file</span>(</span>
<span id="cb72-25">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">path =</span> system_prompt_file,</span>
<span id="cb72-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">audience =</span> audience_content,</span>
<span id="cb72-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length =</span> length_content,</span>
<span id="cb72-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_content,</span>
<span id="cb72-29">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">event =</span> event_content</span>
<span id="cb72-30">)</span>
<span id="cb72-31"></span>
<span id="cb72-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Reusable scoring category</span></span>
<span id="cb72-33">type_scoring_category <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb72-34">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb72-35">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Score from 1 to 10."</span></span>
<span id="cb72-36">  ),</span>
<span id="cb72-37">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">justification =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb72-38">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief explanation of the score."</span></span>
<span id="cb72-39">  ),</span>
<span id="cb72-40">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb72-41">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Concise, actionable improvements, mentioning slide numbers if applicable."</span>,</span>
<span id="cb72-42">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb72-43">  ),</span>
<span id="cb72-44">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">score_after_improvements =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb72-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated score after suggested improvements."</span></span>
<span id="cb72-46">  )</span>
<span id="cb72-47">)</span>
<span id="cb72-48"></span>
<span id="cb72-49"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Top-level deck analysis object</span></span>
<span id="cb72-50">type_deck_analysis <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_object</span>(</span>
<span id="cb72-51">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">presentation_title =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The presentation title."</span>),</span>
<span id="cb72-52">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">total_slides =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Total number of slides."</span>),</span>
<span id="cb72-53">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_code =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb72-54">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing code blocks (0–100)."</span></span>
<span id="cb72-55">  ),</span>
<span id="cb72-56">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">percent_with_images =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_number</span>(</span>
<span id="cb72-57">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Percentage of slides containing images (0–100)."</span></span>
<span id="cb72-58">  ),</span>
<span id="cb72-59">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">estimated_duration_minutes =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_integer</span>(</span>
<span id="cb72-60">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimated presentation length in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide."</span></span>
<span id="cb72-61">  ),</span>
<span id="cb72-62">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tone =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb72-63">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Brief description of the presentation tone (e.g., informal, technical, playful)."</span></span>
<span id="cb72-64">  ),</span>
<span id="cb72-65">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">clarity =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-66">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?"</span>,</span>
<span id="cb72-67">    type_scoring_category</span>
<span id="cb72-68">  ),</span>
<span id="cb72-69">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">relevance =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-70">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Asses how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?"</span>,</span>
<span id="cb72-71">    type_scoring_category</span>
<span id="cb72-72">  ),</span>
<span id="cb72-73">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">visual_design =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-74">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?"</span>,</span>
<span id="cb72-75">    type_scoring_category</span>
<span id="cb72-76">  ),</span>
<span id="cb72-77">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">engagement =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-78">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?"</span>,</span>
<span id="cb72-79">    type_scoring_category</span>
<span id="cb72-80">  ),</span>
<span id="cb72-81">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pacing =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-82">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Analyze the distribution of content across slides. Are some slides too dense or too light? "</span>,</span>
<span id="cb72-83">    type_scoring_category</span>
<span id="cb72-84">  ),</span>
<span id="cb72-85">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">structure =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-86">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?"</span>,</span>
<span id="cb72-87">    type_scoring_category</span>
<span id="cb72-88">  ),</span>
<span id="cb72-89">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">concistency =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-90">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Evaluate whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?"</span>,</span>
<span id="cb72-91">    type_scoring_category</span>
<span id="cb72-92">  ),</span>
<span id="cb72-93">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accessibility =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_array</span>(</span>
<span id="cb72-94">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">description =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?"</span>,</span>
<span id="cb72-95">    type_scoring_category</span>
<span id="cb72-96">  )</span>
<span id="cb72-97">)</span>
<span id="cb72-98"></span>
<span id="cb72-99"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Define a tool to calculate some metrics</span></span>
<span id="cb72-100"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start with a function:</span></span>
<span id="cb72-101"></span>
<span id="cb72-102"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Calculates the total number of slides, percentage of slides with code blocks,</span></span>
<span id="cb72-103"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' and percentage of slides with images in a Quarto presentation HTML file.</span></span>
<span id="cb72-104"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb72-105"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param metric The metric to calculate: "total_slides" for total number of slides,</span></span>
<span id="cb72-106"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' "code" for percentage of slides containing fenced code blocks, or "images"</span></span>
<span id="cb72-107"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' for percentage of slides containing images.</span></span>
<span id="cb72-108"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return The calculated metric value.</span></span>
<span id="cb72-109">calculate_slide_metric <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">function</span>(metric) {</span>
<span id="cb72-110">  html_file <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Quarto/docs/my-presentation.html"</span></span>
<span id="cb72-111">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.exists</span>(html_file)) {</span>
<span id="cb72-112">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(</span>
<span id="cb72-113">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"HTML file does not exist. Please render your Quarto presentation first."</span></span>
<span id="cb72-114">    )</span>
<span id="cb72-115">  }</span>
<span id="cb72-116">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Read HTML file</span></span>
<span id="cb72-117">  html_content <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">readChar</span>(html_file, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.size</span>(html_file))</span>
<span id="cb72-118"></span>
<span id="cb72-119">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Split on &lt;section&gt; tags to get individual slides</span></span>
<span id="cb72-120">  slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">strsplit</span>(html_content, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;section"</span>))</span>
<span id="cb72-121"></span>
<span id="cb72-122">  total_slides <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(slides)</span>
<span id="cb72-123"></span>
<span id="cb72-124">  <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"total_slides"</span>) {</span>
<span id="cb72-125">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> total_slides</span>
<span id="cb72-126">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code"</span>) {</span>
<span id="cb72-127">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides where we see the "sourceCode" class</span></span>
<span id="cb72-128">    slides_with_code <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class="sourceCode"'</span>, slides))</span>
<span id="cb72-129">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_code <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb72-130">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">if</span> (metric <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"images"</span>) {</span>
<span id="cb72-131">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Count slides with image tag</span></span>
<span id="cb72-132">    slides_with_image <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'&lt;img'</span>, slides))</span>
<span id="cb72-133">    result <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>((slides_with_image <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> total_slides) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb72-134">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">else</span> {</span>
<span id="cb72-135">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stop</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown metric: choose 'total_slides', 'code', or 'images'"</span>)</span>
<span id="cb72-136">  }</span>
<span id="cb72-137"></span>
<span id="cb72-138">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return</span>(result)</span>
<span id="cb72-139">}</span>
<span id="cb72-140"></span>
<span id="cb72-141"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Optionally, to avoid manual work:</span></span>
<span id="cb72-142"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># create_tool_def(calculate_slide_metric)</span></span>
<span id="cb72-143">calculate_slide_metric <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tool</span>(</span>
<span id="cb72-144">  calculate_slide_metric,</span>
<span id="cb72-145">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Returns the calculated metric value"</span>,</span>
<span id="cb72-146">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">metric =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type_string</span>(</span>
<span id="cb72-147">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'The metric to calculate: "total_slides" for total number of slides, </span></span>
<span id="cb72-148"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      "code" for percentage of slides containing fenced code blocks, or "images"</span></span>
<span id="cb72-149"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      for percentage of slides containing images.'</span>,</span>
<span id="cb72-150">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">required =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb72-151">  )</span>
<span id="cb72-152">)</span>
<span id="cb72-153"></span>
<span id="cb72-154"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Initialise chat with Claude Sonnet 4 model</span></span>
<span id="cb72-155">chat <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_anthropic</span>(</span>
<span id="cb72-156">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"claude-sonnet-4-20250514"</span>,</span>
<span id="cb72-157">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">system_prompt =</span> system_prompt,</span>
<span id="cb72-158">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">params =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">params</span>(</span>
<span id="cb72-159">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">temperature =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># default is 1</span></span>
<span id="cb72-160">  )</span>
<span id="cb72-161">)</span>
<span id="cb72-162"></span>
<span id="cb72-163"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Register the tool with the chat</span></span>
<span id="cb72-164">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_tool</span>(calculate_slide_metric)</span>
<span id="cb72-165"></span>
<span id="cb72-166"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Start conversation with the chat</span></span>
<span id="cb72-167"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1: regular chat to extract meta-data</span></span>
<span id="cb72-168">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat</span>(</span>
<span id="cb72-169">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interpolate</span>(</span>
<span id="cb72-170">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 1 (counts). Here are the slides in Markdown: {{ markdown_content }}"</span></span>
<span id="cb72-171">  )</span>
<span id="cb72-172">)</span>
<span id="cb72-173"></span>
<span id="cb72-174"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2: structured chat to further analyse the slides</span></span>
<span id="cb72-175">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chat_structured</span>(</span>
<span id="cb72-176">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Execute Task 2 (suggestions)"</span>,</span>
<span id="cb72-177">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> type_deck_analysis</span>
<span id="cb72-178">)</span></code></pre></div>
</details>
</div>
</div>
<div id="tabset-14-3" class="tab-pane" aria-labelledby="tabset-14-3-tab">
<div class="cell">
<details class="code-fold">
<summary>Show full prompt</summary>
<div class="sourceCode cell-code" id="cb73" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb73-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb73-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You are a presentation coach for data scientists that analyses presentation slide decks written in Markdown. </span></span>
<span id="cb73-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract key information, evaluate quality, and return structured feedback that is constructive, focused and practical.</span></span>
<span id="cb73-4"></span>
<span id="cb73-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The presentation you are helping with is a {{ length }}-minute {{ type }} at {{ event }}.  </span></span>
<span id="cb73-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">The audience is {{ audience }}. </span></span>
<span id="cb73-7"></span>
<span id="cb73-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You can be asked for one of the following tasks. Each has a number and a name:</span></span>
<span id="cb73-9"></span>
<span id="cb73-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 1 (counts)</span></span>
<span id="cb73-11"></span>
<span id="cb73-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb73-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The number of slides</span></span>
<span id="cb73-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The percentage of slides containing code blocks</span></span>
<span id="cb73-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The percentage of slides containing images </span></span>
<span id="cb73-16"></span>
<span id="cb73-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">Return only the JSON results, nothing else.</span></span>
<span id="cb73-18"></span>
<span id="cb73-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Task 2 (suggestions)</span></span>
<span id="cb73-20"></span>
<span id="cb73-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You extract the following information:</span></span>
<span id="cb73-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- The presentation title</span></span>
<span id="cb73-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Estimated presentation length (in minutes, assuming ~1 minute per text slide and 2–3 minutes per code or image-heavy slide)</span></span>
<span id="cb73-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Tone (a brief description)</span></span>
<span id="cb73-25"></span>
<span id="cb73-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You score the presentation on the following categories (from 1–10), and give a concise explanation:</span></span>
<span id="cb73-27"></span>
<span id="cb73-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">1. Clarity of content: evaluate how clearly the ideas are communicated. Are the explanations easy to understand? Are terms defined when needed? Is the key message clear?</span></span>
<span id="cb73-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">2. Relevance for intended audience: assess how well the content matches the audience’s background, needs, and expectations. Are examples, depth of detail, and terminology appropriate for the audience type?</span></span>
<span id="cb73-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">3. Visual design: judge the visual effectiveness of the slides. Are they readable, visually balanced, and not overcrowded with text or visuals? Is layout used consistently?</span></span>
<span id="cb73-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">4. Engagement: estimate how likely the presentation is to keep attention. Are there moments of interactivity, storytelling, humor, or visual interest that invite focus?</span></span>
<span id="cb73-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">5. Pacing: analyze the distribution of content across slides. Are some slides too dense or too light? </span></span>
<span id="cb73-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">6. Structure: review the logical flow of the presentation. Is there a clear beginning, middle, and end? Are transitions between topics smooth? Does the presentation build toward a conclusion?</span></span>
<span id="cb73-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">7. consistency: evaluatue whether the presentation is consistent when it comes to formatting, tone, and visual elements. Are there any elements that feel out of place?</span></span>
<span id="cb73-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">8. Accessibility: consider how accessible the presentation would be for all viewers, including those with visual or cognitive challenges. Are font sizes readable? Is there sufficient contrast? Are visual elements not overwhelming?</span></span>
<span id="cb73-36"></span>
<span id="cb73-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">For each of the above scoring categories, provide specific and actionable improvements. Follow these instructions:</span></span>
<span id="cb73-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Keep each suggestion concise and mention the slide number(s) if applicable.</span></span>
<span id="cb73-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Do not invent issues and only suggest improvements when the content would clearly benefit from them.</span></span>
<span id="cb73-40"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- For each catogory, estimate what the new score would be if these improvements are implemented.</span></span>
<span id="cb73-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">- Return the improvement and new score as part of the response for that category.</span></span>
<span id="cb73-42"></span>
<span id="cb73-43"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">You bundle your results with the results from the first task. Always return the result as a JSON object that conforms to the provided data model.</span></span>
<span id="cb73-44"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span></code></pre></div>
</details>
</div>
</div>
</div>
</div>
<p>But there’s one questions that might still be lingering in your mind: how much does this cost?!</p>
</section>
<section id="understanding-tokens-and-costs" class="level1">
<h1>Understanding tokens and costs</h1>
<p>Back to our nightmare from the beginning: a bill with a lot of zeros for using an LLM. Luckily you can prevent this pretty easily and it all starts with understanding how and for what you’re being charged.</p>
<p>Whenever you talk to an LLM, you’re essentially paying for how much you say and how much it says back. Responses are all measured in tokens, those small chunks of text we talked about in Part 1 of this series. For every x amount of tokens, you pay a particular price. For example, at the time of writing, <a href="https://docs.anthropic.com/en/docs/about-claude/pricing">Claude Sonnet 4</a> has a price of 3 USD per million tokens for input, and a price of 15 USD per million tokens for output.</p>
<p>When talking about costs it’s important to understand that you pay for both input and output. That means you’re billed for what you send and for what the assistant replies with. So if you ask a really long question and get a really long answer, that’s going to cost more than a question that requires a yes or no answer.</p>
<p>This is why your prompt and structure matters. If you keep repeating the same context or include an entire Quarto presentation each time you make a call, your token count will add up fast. Remember that the history is send to the LLM with every subsequent question! A deeply nested chat history can cause costs to go up quickly. This is also true for very long system prompts. Therefore, it’s often worth trimming what you send, summarising where possible, and being intentional about what the model really needs to see. There’s a fine balance between giving as much context as possible for the best possible response, and making sure you’re not overdoing it.</p>
<p>Manually keeping track of your tokens would be annoying, so with <code>chatlas</code> or <code>ellmer</code>, you’ve got a few handy functions to help estimate token use:</p>
<ul>
<li><code>get_tokens()</code> gives you a raw count of how many tokens you’ve used so far.</li>
<li><code>get_cost()</code> gives you an estimate of how much your chat might cost. Note how it says “estimate”. Model providers can change their pricing overnight, and there might not even be a heads-up. This estimate therefore serves as a rough guide, not the truth.</li>
</ul>
<p>Now for the real thing you want to know: for our full workflow, we pay around 0.06 USD.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-15-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-15-1" aria-controls="tabset-15-1" aria-selected="true">Python</a></li><li class="nav-item"><a class="nav-link" id="tabset-15-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-15-2" aria-controls="tabset-15-2" aria-selected="false">R</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-15-1" class="tab-pane active" aria-labelledby="tabset-15-1-tab">
<div class="sourceCode" id="cb74" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb74-1">chat.get_tokens()</span>
<span id="cb74-2">chat.get_cost()</span></code></pre></div>
<div class="sourceCode" id="cb75" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb75-1">[{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'role'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'user'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3611</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_cached'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_total'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3611</span>},</span>
<span id="cb75-2"> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'role'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'assistant'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">133</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_cached'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_total'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">133</span>},</span>
<span id="cb75-3"> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'role'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'user'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">102</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_cached'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_total'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3846</span>},</span>
<span id="cb75-4"> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'role'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'assistant'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">52</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_cached'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_total'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">52</span>},</span>
<span id="cb75-5"> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'role'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'user'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2435</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_cached'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_total'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6333</span>},</span>
<span id="cb75-6"> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'role'</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'assistant'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">947</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_cached'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'tokens_total'</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">947</span>}]</span></code></pre></div>
<div class="sourceCode" id="cb76" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb76-1"><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.058350000000000006</span></span></code></pre></div>
</div>
<div id="tabset-15-2" class="tab-pane" aria-labelledby="tabset-15-2-tab">
<div class="sourceCode" id="cb77" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb77-1">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_tokens</span>()</span>
<span id="cb77-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;1      user   3552         3552</span></span>
<span id="cb77-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;2 assistant    148          148</span></span>
<span id="cb77-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;3      user    101         3801</span></span>
<span id="cb77-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;4 assistant     48           48</span></span>
<span id="cb77-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;5      user   1839         5688</span></span>
<span id="cb77-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;6 assistant    932          932</span></span>
<span id="cb77-8"></span>
<span id="cb77-9">chat<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_cost</span>()</span>
<span id="cb77-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;[1] $0.06</span></span></code></pre></div>
</div>
</div>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Costs differ between runs!
</div>
</div>
<div class="callout-body-container callout-body">
<p>Token counts and cost vary because the answers of the LLM vary too (thanks to the creativity of the model).</p>
</div>
</div>
<p>In our case, we might be working with pretty long Quarto presentations. The bigger the presentation, the more tokens our input will have. So far, we only played around with a demo presentation for a 10-minute talk, and basically put everything in one prompt. But we might have subsequent questions to the LLM, too. Overall, there are two strategies that can be used to reduce token count and cost:</p>
<ol type="1">
<li>Batching: instead of sending multiple small calls, combine them into a single, well-structured one when possible. It reduces overhead and often results in fewer total tokens. Keep it DRY to: don’t repeat yourself by providing information twice. For example, when using structured output it’s not strictly necessary to put all the specifications for the output in the prompt itself too. And it shouldn’t be a surprise, but batching is also possible in <code>ellmer</code> with <a href="https://ellmer.tidyverse.org/reference/batch_chat.html"><code>batch_chat()</code></a> and (soon!) in <code>chatlas</code> with a similar function.</li>
<li>Summarising: if you’re feeding in the same context repeatedly, have the model summarise it first. Then reuse that summary instead of the full text. It’s a small change that can lead to big savings.</li>
</ol>
<p>Every token counts. And if you want to build something that scales, getting a grip on your usage early can save you a lot down the road.</p>
</section>
<section id="whats-next" class="level1">
<h1>What’s next</h1>
<p>The output that we are currently receiving is nice, but it’s not something that would amaze our users. For us humans, it’s annoying to read information from JSON as well. We need something different. To really get a wow-effect we need to design an appealing user interface. We need to make it easy for users to upload their content, give us additional information (aka our required variables), and analyse the results. Nobody likes a boring static app, so we need to add some shiny interactive features as well. And those shiny interactive features? That’s a perfect job for Shiny!</p>
<p>In this part of “The Shiny Side of LLMs” we built a foundation for our Shiny app: using either <code>chatlas</code> (Python) or <code>ellmer</code> (R) we can now analyse any Quarto presentation. Our current workflow allows us to:</p>
<ul>
<li>Convert a Quarto file to plain markdown (and HTML), which can easily be consumed by an LLM</li>
<li>Call any LLM from any (or ok, most) providers through their API (our chosen model being Claude from Anthropic). Thanks <code>chatlas</code> and <code>ellmer</code>!</li>
<li>Send a carefully crafted prompt that guides the LLM to analyse the markdown content</li>
<li>Let the LLM use a tool to accurately calculate some metrics</li>
<li>Tweak model parameters if we really want to go the extra mile</li>
<li>Receive consistent, structured and useful JSON output that we can use in our Shiny app</li>
</ul>
<p>The only thing left to do? Wrap it in that Shiny interface. See you in the third and last part of “The Shiny Side of LLMs” series!</p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/</guid>
  <pubDate>Fri, 05 Sep 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-2/shiny-side-of-llms-header.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>What LLMs Actually Do (and What They Don’t)</title>
  <dc:creator>Veerle Eeftink - van Leemput</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-header.png" alt="The Shiny Side of LLMs part 1" style="width: 100%; height: auto;">
    </div>
    


<section id="welcome-to-the-shiny-side-of-llms" class="level1">
<h1>Welcome to the Shiny Side of LLMs!</h1>
<p>Large language models (LLMs) are everywhere, and we’re way beyond denying their existence. New use cases pop up every single day, from autocompleting code to customer support bots, summarisers, and tutors. And everybody feels the urge to jump in. Tools like ChatGPT, Claude, and GitHub Copilot have made us all comfortable using AI. But when it comes to building something that is tailored, interactive, and actually useful for others, it all starts to feel… muddy. Buzzwords start flying: embedding, encoding, RAG, vector databases, streaming tokens… and suddenly you’re wondering if this is still for you.</p>
<p>Spoiler alert: it is, and it’s all more accessible than it seems. Yes, the underlying technology is complex, but that doesn’t mean it’s out of reach. For the first time, you have easy access to something that mimics human reasoning. As a programmer, that means you can describe a problem in plain language and get useful output: code, explanations, ideas, or even full analyses. And you don’t need to train your own models or master deep learning theory to use it. You just need to understand what’s happening under the hood well enough to start building. In this blog series, we’ll demystify all the buzzwords and walk you through:</p>
<ul>
<li>What LLMs really do, conceptually and technically, but explained super simply</li>
<li>How to interact with them while staying in your comfort zone (Python or R)</li>
<li>How to turn those interactions into real, usable tools using Shiny (again: Python or R)</li>
<li>The magic of mixing in your own data such as documents, notes, and anything else you use</li>
</ul>
<p>This isn’t just about building a chatbot. It’s about understanding how things work and how you can use an LLM to build your own tools. A document summariser, a smart search assistant, or a decision helper for your team: the choice is yours. We’ll go step by step. From “zero” (I’ve only ever typed things into ChatGPT) to “hero” (I’ve built my own LLM-powered app and I understand how it works).</p>
<p>No need to know anything about model hosting, server clusters, or fancy acronyms. Just you, some Python or R, and a fun idea!</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
What can you expect from this first part?
</div>
</div>
<div class="callout-body-container callout-body">
<p>This is Part 1 of “The Shiny Side of LLMs” series: <em>What LLMs Actually Do (and What They Don’t).</em> It is intended to give you some insight into how LLMs work under the hood.</p>
<p>You don’t <em>need</em> to know all this to start building things. But having a basic understanding of the mechanics can help you reason more clearly about what LLMs are (and aren’t) good at and avoid getting lost in the hype.</p>
</div>
</div>
</section>
<section id="the-large-the-language-and-the-model" class="level1">
<h1>The “Large”, the “Language”, and the “Model”</h1>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<p>When we talk about a ”Large Language Model”, we’re really talking about a system trained on huge amounts of text, designed to predict and generate human-like language, powered by a model that recognises patterns at scale.</p>
</div>
</div>
<p>First things first, what is a Large Language Model? A good first step is breaking down the term “Large Language Model”. Let’s do it backwards, starting with the last word: ”Model”. In this context, a model is a statistical system that learns patterns from data and uses them to make predictions. Yes, that’s right: plain old statistics.</p>
<p>When we’re talking about a “Language Model” we make predictions about a language, consisting of sentences and its words. It all boils down to the likelihood of a next word, or sequence of words. A language model is trained to understand the kinds of words that tend to appear together, how sentences flow, and how meaning is built from context. Early models could only guess a single word at a time and struggled with anything longer or more complex, but modern ones can generate entire paragraphs or documents that stay on topic and sound surprisingly coherent. Almost like a human.</p>
<p>Bigger is better. The “Large” in “Large Language Model” refers to both the amount of data the model is trained on and the number of parameters it has (the internal settings the model adjusts as it learns). The “larger” a model is, the more patterns it can recognise and the more fluent its outputs tend to be.</p>
<p>So what does that mean when you interact with one? When you ask an LLM something, you prompt it to do something (that’s why the input or text you type is also called a prompt). That something is to predict the most likely sequence of words based on the sequence you have given.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
LLMs and AI
</div>
</div>
<div class="callout-body-container callout-body">
<p>You didn’t see much mention of Artificial Intelligence (AI) yet, and that’s on purpose. While LLMs are a specific kind of AI, they’re just one part of a much broader field. But since LLMs are the most visible (and frankly, the most hyped) examples of AI right now, people often use the terms interchangeably. To keep things focussed, we’ll only use the term LLM here.</p>
</div>
</div>
</section>
<section id="how-llms-learn" class="level1">
<h1>How LLMs learn</h1>
<p>We can talk about LLMs like they’re the newest thing, but the foundation is still as “ancient” as statistics. It’s about understanding data: making inferences, finding patterns, estimating (un)certainty. Machine Learning (ML) is statistics, and ML models learn patterns from data. Think about algorithms like decision trees, support vector machines, and linear regression that all rely on the same idea: learning from examples.</p>
<p>LLMs learn through a technique called deep learning, which is a subfield of ML. At the core of deep learning are neural networks: mathematical structures loosely inspired by biological neurons within the brain. You can think of them as layers of tiny decision-makers, where each layer takes in numbers, transforms them, and passes them on to the next.</p>
<p>Each connection between layers has a weight, aka a number that determines how strongly one “neuron” influences another. During training, the model adjusts these weights millions (or billions) of times to reduce the gap between its predictions and the actual data. This process is called back propagation, and it’s basically a giant optimisation loop: the model makes a guess, sees how wrong it was, tweaks the weights, and tries again. Over time, it gets surprisingly good at spotting patterns in language.</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-back-propagation-1.png" class="img-fluid"></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-back-propagation-2.png" class="img-fluid figure-img"></p>
<figcaption>A simplified explanation of back propagation in neural networks</figcaption>
</figure>
</div>
<p>The bigger the model, the more opportunities it has to learn how to make good guesses. That’s why the “Large” in “Large Language Model” is so important. In the last few years, model size and model capability has exploded. This is due to hardware improvements (something has to crunch all that data), better training techniques and something called transformers. Ah, jargon.</p>
<p>A transformer is a kind of model architecture. “Model architecture” sounds difficult, but in simple terms this is a blueprint for how a model processes and learns from data. It was introduced by <a href="https://arxiv.org/pdf/1706.03762">Vaswani et al.&nbsp;(2017)</a>. Transformers are part of the neural network family, and they’re designed to handle huge amounts of data quickly and efficiently, which makes them perfect for crunching lots of text.</p>
<p>What made transformers such a game-changer is a clever idea called attention. Instead of reading text word by word like older models, transformers look at the whole sentence (or paragraph) at once and figure out which words matter most to each other. That’s how large language models can generate surprisingly accurate, context-aware text.</p>
<p>They also made it possible to train on much bigger chunks of data and pick up more subtle patterns in language. This is combined with a technique called self-supervised learning, where the model learns to predict missing words in sentences. If you show it “I lifted some ___” it learns that “weights” is more likely than “horses”. Because the data itself provides the training signals, we don’t need humans to label anything. That makes training scalable. It doesn’t mean it’s totally label-free, though. The “labels” (like missing words) just come naturally from the text.</p>
<p>The combo of smart architecture and massive training data is what unlocked the big leap in quality of today’s LLMs and made models like ChatGPT possible. Fun fact: the “T” in GPT stands for Transformer. Now you know why that is!</p>
<p>From statistics to transformers, to models we can chat with and help us to get things done faster. How’s that for the “ancient” statistical techniques?!</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-stats.png" class="img-fluid figure-img"></p>
<figcaption>It all starts with statistics</figcaption>
</figure>
</div>
<p>It all sounds too good to be true: a model that learns on its own, getting “smarter” with every chunk of input you provide. And it indeed is, because with this self-supervised learning come challenges too. As more online text is generated by LLMs, there’s a risk the model ends up learning from itself. It’s like copying a copy: input gets blurry. And in the end LLM are just models that predict something based on the data it has been given. If that data is garbage, so is the outcome. If we don’t want to end up with boring, soulless, similar, and even incorrect content, we still have to write content ourselves.</p>
<p>Still, one thing is clear: what makes LLMs so powerful is the amount of data they learn from. And that leads us straight into the magic of scale.</p>
</section>
<section id="the-magic-of-scale" class="level1">
<h1>The magic of scale</h1>
<p>As LLMs are trained on more and more data, something cool happens: they begin to show emergent abilities (<a href="https://doi.org/10.48550/arXiv.2206.07682">Wei et al., 2022</a>). These are skills the model wasn’t directly trained for, yet still manages to do. Nobody explicitly programmed an LLM to summarise text, translate between languages, or generate code. But once the model is large enough and has seen enough examples, these abilities just… emerge. Magic!</p>
<p>This is different from what we call generative abilities. “Generative” simply means the model can generate new text. All LLMs are generative and can produce new text. You give them a prompt, and they predict what comes next. That’s their basic job and it’s expected behaviour. Emergent abilities were not in the original job description and the model just suddenly gets good at tasks nobody told it to do. It’s surprising behaviour.</p>
<p>For example, you can ask it to convert a sentence into pirate-speak, even if it was never explicitly trained to do that. It uses general language patterns it has learned to give it a try, which is known as zero-shot learning. And if the model struggles to understand what you want, you can help it by giving a few examples in your prompt: few-shot learning. If you want a list reformatted in a certain way, you can provide some examples and let the LLM finish the rest.</p>
<p>All of this is possible not because the model is “intelligent” in a human sense, but because it has seen so much text and learned such general patterns that it can apply them in all kinds of situations. This ability to generalise, or doing more with less instruction, is one of the most powerful outcomes of large-scale learning.</p>
</section>
<section id="what-llms-arent" class="level1">
<h1>What LLMs aren’t</h1>
<p>So you might think LLMs are pretty smart. After all, they can write essays, answer questions, and even debug your Python and R code. It’s the shiny side of LLMs. But it isn’t all rainbows and unicorns: LLMs aren’t smart and they aren’t thinking. LLMs don’t reason like humans. They don’t understand what they’re saying. They don’t “know” facts in the way we do. What they do is predict the next word in a sentence, based on patterns they’ve seen in massive amounts of text.</p>
<p>That means an LLM can be confidently wrong. They might “hallucinate” an answer that sounds plausible but is completely made up. The code it generates might look solid, but when you actually run it you notice that it contains packages that don’t exist or variable names that were never defined. They might reflect or even amplify biases in their training data. They might sound accurate, but miss the mark entirely. And crucially, you don’t really control them. You can’t force an LLM to always give a safe or correct answer. What you can do is guide it with better prompts, build tools around it that check its output, and design systems that use the model responsibly.</p>
<p>So while LLMs are great at generalising across tasks, that power doesn’t come from intelligence. It comes from scale. And that is important to remember when building with LLMs.</p>
</section>
<section id="lets-talk-about-context" class="level1">
<h1>Let’s talk about context</h1>
<p>As mentioned earlier, LLMs can understand and generate surprisingly accurate, context-aware text. And that context deserves some attention (literally: remember transformers!). It’s the context that makes sure an LLM doesn’t blurt out random sentences (or, ok, only occasionally do). But what does “context” actually mean for a Large Language Model?</p>
<p>It all starts with tokens, which are tiny chunks of text. A token can be a word, a part of a word, or even just characters or punctuation. Every time you type something into an LLM, your input is split into these tokens. Depending on the model, an LLM can “see” a certain number of tokens at once. This is called the context window. Think of it like a sliding window over a long scroll of text. The model can only see what fits inside that window. If your input is too long and tokens fall outside of that window: too bad. An LLM might trims the beginning or the end and hopes you didn’t put anything important there. The exact behaviour varies by provider and how the system around the model is designed. Some providers implement strategies like summarising or condensing previous conversation history, while others simply drop older messages to make room for new ones. And in some cases, the system might reject the request outright rather than attempt to process an incomplete prompt.</p>
<p>But wait… We previously learned LLMs were trained on all of language right? Yes. But at generation time, it doesn’t remember everything it’s ever seen. It only has this input, right now, and the tokens inside the window. That’s its entire world. There’s no long-term memory (unless you’ve built that in, we’ll get to that later). So if you want the model to give you a good answer, what you say really matters. And that’s not the only thing: it also matters how you say it.</p>
<p>Imagine these two prompts:</p>
<pre><code>1. “Create a Shiny for Python app that plots a histogram of random numbers”
2. “Plot a histogram of random numbers. Use Shiny for Python.”</code></pre>
<p>Same words. Different order. Potentially different behaviour.</p>
<p>Language models are super sensitive to the flow of a sentence. If you put instructions at the start, the model is more likely to follow them. Bury them at the end, and it might treat it more like a side-note. Clarity is an LLMs best friend.</p>
<p>So context isn’t just about what’s in the window. It’s also about where words are and how they’re phrased. Order and surrounding words matter. In the model’s brain, each token looks around (thanks to that attention trick you met earlier) and asks, “who’s standing next to me, and what story are we telling?” Those neighbouring words:</p>
<ol type="1">
<li><strong>Disambiguate meaning:</strong> words on their own only say so much. “Bass” is a fish next to “river”, and a guitar next to “band.”</li>
<li><strong>Signal tone:</strong> a single word can flip the sentiment. For example, “nice” after “not”. On its own, the word “nice” would be positive.</li>
<li><strong>Set expectations:</strong> since a model is trained on sequences of tokens actually written by humans, some sequences are familiar. So after “Once upon a” the model is primed for “time,” not “ship”.</li>
</ol>
<p>Because the model predicts the next word by weighing everything it can see in the window, every surrounding word tweaks the odds. Change one word, and the whole probability landscape shifts. This becomes clear with these different prompts:</p>
<pre><code>1. “Write a short horror story about a clown”
2. “Write a funny short horror story about a clown”</code></pre>
<p>Can you already sense why “prompt engineering” became so hot?</p>
<p>Now back to the memory thing. Out of the box, the model doesn’t “remember” what you said three chats ago. It doesn’t even remember what you said two messages ago, unless you send the whole chat history in the current prompt. So how big a prompt can get, determines how much an LLM can remember. This brings us back to the context window. The maximum number of tokens they can handle at once matter. It could be 2000 or 128000 tokens. That all depends on the model. If your conversation gets longer than the model can hold, the oldest messages get chopped off. That’s why in very long chats, you might notice an LLM starts “forgetting” early facts. These facts are not in the context window anymore, so the model can’t “see” them anymore either.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-chat.png" class="img-fluid figure-img"></p>
<figcaption>Ok, this isn’t a “very long” chat, but you get the idea: whatever is outside the context window is “lost” information</figcaption>
</figure>
</div>
<p>It is possible for models to have access to persistent memory across conversations, but in that case facts and knowledge have to be explicitly stored somewhere. This memory is separate from the context window. Whether and how that memory gets updated depends on the broader system designed by the LLM provider. This system, the surrounding infrastructure and logic around the model, determines what’s worth saving. Only if something seems important enough the memory will be updated.</p>
</section>
<section id="from-tokens-to-embedding-to-encoding" class="level1">
<h1>From tokens to embedding to encoding</h1>
<p>Your prompt is divided into tokens, those tiny chunks of text. But computers don’t “understand” words like we do. Computers work with numbers. That’s where something fancy called “embedding” comes in: each token is turned into numbers that represents its meaning. And these numbers are not a plain “4982” or “2974”, but rather a pattern of numbers. You can compare it with an unique barcode. To “understand” your prompt, the model looks at all these number patterns together and uses the earlier mentioned attention to figure out how they relate to each other and what the relationship between the words is. This process is called encoding. A well-known model that does this is BERT, which was introduced by Google in 2018 (<a href="https://doi.org/10.48550/arXiv.1810.04805">Devlin et al., 2019</a>).</p>
<p>Because of word embeddings and encoding, a prompt like “Create a Shiny for Python app that plots a histogram” is not just a set of random words. The model knows that “Shiny” and “Python” relate to things like “web app” or “dashboard” because their embeddings are close together. It gives the model directions on the tech stack to use. It also sees that “plot” and “histogram” are both in the prompt, so it understands you want a chart. And the word “create” signals that you’re asking for code, not just an explanation. All these relationships, links and signals are taken into account when generating the output token by token.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-code-gen.png" class="img-fluid figure-img"></p>
<figcaption>From input to output: how an LLM generates code</figcaption>
</figure>
</div>
<p>So far, we only talked about generating language, like plain text or some code. But what about images? The latest models can take images as input and they spit out a polished (perhaps fake-looking) image when being asked too. These multimodal models (like GPT-4o or Claude 3) combine both vision and language. How? By treating images like language!</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Images and multimodal models
</div>
</div>
<div class="callout-body-container callout-body">
<p>When talking about images, we’re taking a small side-step in our LLM journey. Image tasks are typically handled by specialised models (like diffusion models for image generation) or by multimodal LLMs (like GPT-4o or Gemini) that can process both text and images. So when we talk about “LLMs” generating or understanding images, we’re really referring to LLMs with extra capabilities.</p>
</div>
</div>
<p>Just like text is broken down into tokens, images are also converted into a form the model can understand. Instead of words, an image is split into patches (tiny chunks of pixels). You can think of this as “tokenizing an image.” And just like tokens, patches are turned into numbers that capture the meaning of those pixels, which brings us back to the fancy word: “embedding”. These image embeddings are then processed by the model just like text embeddings. That’s how a model can look at an image and describe it or answer questions about it.</p>
<p>Generating new images is also not a problem: in that case embeddings are translated to a visual representation (pixels) and a full image is being built from it. This is exactly what well-known generative vision models like DALL·E, Midjourney, or Stable Diffusion do: they’re trained to turn a sequence of text tokens into a sequence of image tokens, gradually “painting” a picture that matches the prompt. If we ask Stable Diffusion for “A weightlifting Dalmatian holding a barbell with red plates”, it has no problem doing so. It’s all about processing the embeddings together and figure out how every word relates to each other. In this case, “weightlifting” modifies what the “Dalmatian” should look like, and “holding” links “Dalmatian” and “barbell”.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-image-gen.png" class="img-fluid figure-img"></p>
<figcaption>Generating images works pretty similar to generating language for LLMs with those extra capabilities</figcaption>
</figure>
</div>
<p>Note that depending on the model you’re using, you might need to provide more context in your prompt by saying something like “Generate an image that shows…”. This is especially important for multimodal models, like GPT-4o or Gemini, which can generate text, images, audio, and more. A prompt like “A weightlifting Dalmatian holding a barbell with red plates” might seem clear to you and me, but to a multimodal model, it could mean several things: perhaps you want an image, or a description, or a funny meme. This is different for a model like Stable Diffusion, a text-to-image model, which is trained specifically for image generation. Naturally it will assume you want an image when you give it a prompt.</p>
</section>
<section id="more-jargon-rag-and-vector-databases" class="level1">
<h1>More jargon: RAG and vector databases</h1>
<p>LLMs can only generate such “human-like” language because they are good at embeddings, encodings, and understanding context. Context is everything. This context comes from the “knowledge” an LLM has, or from you. On their own, LLMs can only use data that has been used during training to generate output. LLMs only “know” facts that happened during their training process (and not after), and proprietary information is off limits. That doesn’t mean you can’t let an LLM use new facts, or private company info. You can add it to the context, taking the context window into account. So what do you do when you want to add so much information that it doesn’t fit that window? Hello, RAG!</p>
<p>It’s probably not the first time you encountered the term “RAG”. RAG stands for Retrieval-Augmented Generation (<a href="https://doi.org/10.48550/arXiv.2005.11401">Lewis et al., 2020</a>). It means that instead of relying only on what the language model was trained on, it first retrieves relevant information (like from a document or database) and then generates a response using both your prompt and that retrieved info. This helps the model give more accurate and up-to-date answers.</p>
<p>“Retrieving information” sounds simple when you just have one document, but imagine you have a big pile of documents. That’s a bit more complicated. To make retrieval easier, RAG systems usually break documents into smaller chunks, like paragraphs or even sentences. Each chunk gets turned into a vector and stored in a vector database. When you ask a question, the system searches for the chunks whose vectors are closest in meaning to your query and uses those to be added to the context. It’s all about making the most of the context window.</p>
</section>
<section id="so-many-models" class="level1">
<h1>So many models</h1>
<p>We’re not talking about one big giant LLM in this blog series. It’s plural: LLMs. That’s because there are a lot of different LLMs out there. All these models are trained a little differently. That’s important, because how a model is trained (what and how many data it saw, what tasks it practiced, what architecture it uses) shapes what it’s good at. Some models are trained mostly on text (think books, articles, web pages), while others are trained more heavily on code (like GitHub repos). That’s why some models might be better suited as your coding-companion, and others may serve you well as your ghostwriter.</p>
<p>There are many ways to compare LLMs, but here are few things you might want to consider:</p>
<ul>
<li><strong>Context window</strong>: larger windows help with long documents or multi-turn conversations.</li>
<li><strong>Training data</strong>: what kind of data the model was exposed to (e.g.&nbsp;text, code, math problems, dialogue, or some mix).</li>
<li><strong>Instruction tuning</strong>: some models are trained to follow prompts more precisely, often using human feedback (this has a very lengthy name: Reinforcement Learning from Human Feedback, aka RLHF).</li>
<li><strong>Output quality</strong>: ah, there it is! Probably the the most important thing. How good are the model’s answers?! This depends on everything above, plus things like formatting ability, reasoning steps, or hallucination rates.</li>
<li><strong>Cost and speed</strong>: we’ll come back when actually talking to LLMs, but not all models are priced equally, and speed can vary a lot.</li>
</ul>
<p>Since output quality is probably one of your top priorities, you might wonder how you can quickly get a sense of LLM performance. You can try out different models, sure. But as a data-savvy person you’re probably keen to turn towards a more objective method. Luckily, there are tools like <a href="https://llm-stats.com/">llm-stats.com</a>, <a href="https://www.vellum.ai/open-llm-leaderboard">Vellum’s leaderboard</a>, and <a href="https://lmarena.ai/leaderboard">LM Arena</a> that help you with a comparison. The latter is based on subjective human ratings of output quality across tasks.</p>
<p>Looking at these leaderboards you see all the popular models like GPT (OpenAI), Claude (Anthropic), Gemini (Google), Llama (Meta), DeepSeek, Mistral and Nemotron (NVIDIA). And all of these models have different versions too! It might spin your head. Each model has strengths and weaknesses. Some are better with long input, some are cheaper, some are open source… But honestly, don’t stress too much about picking the “perfect” one. Once you start integrating an LLM into your Shiny app, it’s easy to change models later if you need to. At this stage, it’s more important to just get started.</p>
<p>When it comes to personal experience and “word on the street” from Python and R developers: with Claude you’re generally good when it comes to writing code (e.g.&nbsp;as a copilot) and summarising text. If you’re looking for a model with a large context window, Gemini might be what you would be after. And your friendly neighbourhood LLM could potentially be found in GPT models (like GPT-4o) which generally have a good balance between reasoning, code, and creative tasks.</p>
</section>
<section id="building-with-llms" class="level1">
<h1>Building with LLMs</h1>
<p>LLMs can be applied in a wide range of natural language tasks. They’re perfect for building chatbots or virtual assistants. They can generate content, which can be blog posts, emails, or code snippets. They can handle machine translation across languages, summarise long reports into key points, and perform sentiment analysis to extract opinions or emotional tone from text. They even can do data analysis for you.</p>
<p>It’s worth recognising that the examples above can have multiple use cases: you can use LLMs to increase your own productivity, or you can move from using an LLM to building with one. As a copilot, an LLM can help you write code, draft content, summarise documents, or generate ideas, which (hopefully) makes you faster and more efficient in your day-to-day work. But when you start building tools for others, the focus shifts. Now it’s not just about what the model can do, but how you wrap it in a helpful interface. You need to think about the user’s problem, how they interact with your tool, and how to guide their input to get meaningful results. That’s a different kind of challenge. And this is exactly where something like Shiny shines. Whether you’re using Python or R, Shiny makes it easy to build interactive, user-friendly applications that connect to an LLM behind the scenes. You can quickly prototype ideas, test interfaces, and deliver real value. It’s the perfect bridge between your data science skills and creating practical tools powered by LLMs. The only thing you need is a bit of creativity, some coding skills, and a foundational understanding of LLMs. The latter we tackled already!</p>
<p>So what are we going to build in this blog series? Imagine you need to present something to your colleagues in the development team, you’re a little nervous and would like to have some feedback on the presentation first. Since you’re a data scientist who loves reproducible slides, you’ve chosen to build a <a href="https://quarto.org">Quarto presentation</a>. Wouldn’t it be wonderful if you could upload your slides in an app and get instant feedback? Like the perfect “Presentation Rehearsal Buddy” that analyses your slides and gives you tailored suggestions? And wouldn’t it be great if you can help other data scientists with the same problem? You can even prevent your colleagues from giving too lengthly and unfocussed presentations. Also, it’s great for polishing your talks at conferences, like <a href="https://posit.co/conference/">posit::conf(2025)</a>. Now that’s a cool app!</p>
<p>And it’s the kind of thing you can actually start building now you understand what LLMs can and can’t do. But wait… I can feel your creativity flowing, the app idea growing on you, and I understand you might be dreaming bigger. Maybe a model trained on your own (sensitive) company data to really tailor presentations to your work? Amazing! You probably heard it’s not the best idea to send sensitive data to just any public API, so your mind jumps to a local LLM: one that runs on your own machine(s), just for you. A private model that knows everything about your org… That’s tempting! And while it sounds wonderful, it’s probably not the best place to start.</p>
<p>Running your own LLM comes with lots of complexity: infrastructure, cost, fine-tuning, and security. It’s a bit like buying a race car before learning to drive, then trying to build the track and engine from scratch. A better place to begin? Use the best pre-trained models that are already available.</p>
<p>And remember the magic of scale: LLMs show their real power only when trained on massive amounts of data and compute. That’s where those emergent abilities come from. Why reinvent the wheel when you can just take it for a spin? There’s a reason why the best performing models can’t be run on a laptop!</p>
</section>
<section id="responsible-use-and-regulations" class="level1">
<h1>Responsible use and regulations</h1>
<p>Ready, set, … disclaimers. Damn. This is not an ethics class. We’re here to code. But we can’t get around some disclaimers, and if you’re using an LLM, you should take some things into account when it comes to responsible use:</p>
<ul>
<li>Perhaps this goes without saying, but you can’t bluntly copy and paste data or (sensitive) information into any LLM. That means your own data, and somebody else’s data. Generated or uploaded input (text, data, prompts, images, etc.) could (and will) be used for other purposes, such as the training of AI models. If you don’t want things to be out in the open, you shouldn’t input it unless you’re really sure that data will not be re-used.</li>
<li>You can’t trust an LLM completely either. You must always take a critical approach to using the output produced and be aware of the limitations, such as bias, hallucinations and inaccuracies.</li>
<li>You’re accountable for the integrity of the content generated by or with the support of an LLM. There’s also an important difference between <em>using</em> an LLM for your own tasks and <em>building</em> tools on top of LLMs that others will rely on. In the first case, you’re the human in the loop, judging what to trust and what to ignore. But when others interact with your app, you’re responsible for the entire experience: how the model is prompted, how outputs are presented, and whether users are warned of uncertainty. In any case, if you use an LLM to make decisions or build an app that others use to act upon, you can’t blame the LLM for giving false directions.</li>
</ul>
<p>Many organisations know this too, and it makes them wary of using LLM-based tools. Especially in regulated environments, there’s a natural hesitation to adopt these tools without strong safeguards in place. If you want your organisation to be open to the idea, you need to think about clear data handling policies, usage boundaries, a transparent explanation of how the model is prompted, what kind of outputs are produced, and where the limitations lie. The more control and clarity you can offer, the more trust you’ll build.</p>
<p>If your organisation already trusts a cloud vendor like AWS, Azure, or Google Cloud with your data, then using an LLM hosted by that same vendor often fits within existing security and compliance frameworks, which might make it easier to justify adoption.</p>
<p>It’s also good to be aware of applicable regulations and guidelines. Know what you can and can’t use an LLM for when building your application. For example, in Europe, there’s the <a href="https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai">AI Act</a> (which is the first-ever legal framework on AI). Generally speaking, legislation is not that fast, but since AI capabilities are quickly expanding it’s only a matter of time before rules and guidelines do catch up. Stay informed!</p>
<p>And with those less fun but necessary warnings we wrap up this introduction to LLMs. It’s not a magic all-mighty black box anymore: you now know exactly what LLMs are and what they are not.</p>
</section>
<section id="coming-up-talking-to-llms" class="level1">
<h1>Coming up: talking to LLMs</h1>
<p>Our app for polished Quarto presentations is just around the corner, but before we can build it, we need to learn how to actually talk to an LLM. Programmatically that is. After all, the magic only happens once you know how to send the right prompts, pass along the right content, and make sense of what comes back. In our case, that means: extracting a presentation’s content, feeding it to the model with smart prompts, and formatting the output into something useful. Think about value boxes, improvement tips, or even edits to your Quarto file directly. That’s where we’re headed. But first, the next step in our app-building journey: talking to an LLM.</p>
</section>
<section id="references" class="level1">
<h1>References</h1>
<p>Devlin, J., Chang, M. W., Lee, K., &amp; Toutanova, K. (2019). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. <em>arXiv.</em> <a href="https://doi.org/10.48550/arXiv.1810.04805" class="uri">https://doi.org/10.48550/arXiv.1810.04805</a></p>
<p>Lewis, P., Perez, E., Piktus, A., Petroni, F., Karpukhin, V., Goyal, N., Küttler, H., Lewis, M., Yih, W., Rocktäschel, T., Riedel, S., &amp; Kiela, D. (2020). Retrieval‑augmented generation for knowledge‑intensive NLP tasks. <em>arXiv</em>. <a href="https://doi.org/10.48550/arXiv.2005.11401" class="uri">https://doi.org/10.48550/arXiv.2005.11401</a></p>
<p>Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, Ł., &amp; Polosukhin, I. (2017). Attention is all you need. <em>arXiv</em>. <a href="https://doi.org/10.48550/arXiv.1706.03762" class="uri">https://doi.org/10.48550/arXiv.1706.03762</a></p>
<p>Wei, J., Tay, Y., Bommasani, R., Raffel, C., Zoph, B., Borgeaud, S., Yogatama, D., Bosma, M., Zhou, D., Metzler, D., Chi, E. H., Hashimoto, T., Vinyals, O., Liang, P., Dean, J., &amp; Fedus, W. (2022). Emergent Abilities of Large Language Models (Version 2). <em>arXiv.</em> <a href="https://doi.org/10.48550/arXiv.2206.07682" class="uri">https://doi.org/10.48550/arXiv.2206.07682</a></p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/</guid>
  <pubDate>Thu, 31 Jul 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-side-of-llms-part-1/shiny-side-of-llms-header.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Posit at SciPy 2025</title>
  <dc:creator>Shiny Team</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-at-scipy-2025/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <video muted="" autoplay="" playsinline="" style="width: 100%; height: auto;">
        <source src="scipy-og.mp4" type="video/mp4">
        Your browser does not support the video tag.
      </video>
    </div>
    


<div class="{lead}">
<p><strong>The Shiny Team was at SciPy 2025!</strong> If you missed us at <a href="https://www.scipy2025.scipy.org/">SciPy 2025</a> here’s what we were up to.</p>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-at-scipy-2025/scipy-posit-shiny-team.jpg" class="img-fluid figure-img"></p>
<figcaption>posit shiny team at our scipy booth</figcaption>
</figure>
</div>
<p>We just got back from a week at <a href="https://www.scipy2025.scipy.org/">SciPy 2025</a> and had a great time meeting and talking with folks at the conference. If you didn’t get a chance to say hi to us, you can also find us online on <a href="https://github.com/posit-dev/py-shiny"><i class="bi bi-github"></i> GitHub</a> or <a href="https://discord.com/invite/yMGCamUMnS"><i class="bi bi-discord"></i> Discord</a>. You can also learn more about Posit at <a href="https://posit.co"><i class="bi bi-house-fill"></i> posit.co</a>.</p>
<p>If you missed a tutorial or talk we have all the information below. You’ll have to wait a bit for the SciPy folks to get all the tutorials and talks uploaded to <a href="https://www.youtube.com/@SciPy-Conf/videos"><i class="bi bi-youtube"></i> Youtube</a>.</p>
<section id="talks-by-posit-folks" class="level2">
<h2 data-anchor-id="talks-by-posit-folks">Talks by Posit Folks</h2>
<div id="listing-talks" class="quarto-listing quarto-listing-container-custom">
<div class="grid">
  
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://cfp.scipy.org/scipy2025/talk/HEHW8W/" class="text-decoration-none" target="_blank">
                    Shiny for Python: Building Production-Ready Dashboards in Python
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="d-flex align-items-start mb-2">
                  <div class="me-2">
                    <i class="bi bi-person-fill"></i>
                  </div>
                  <div class="d-flex flex-column">
                    <span class="fw-bold">Speaker</span>
                    <div>Daniel Chen</div>
                  </div>
                </div>
              </div>

<p>Shiny is a framework for building web applications and data dashboards in Python. In this workshop, you will see how the basic building blocks of shiny can be extended to create your own scalable production-ready python applications.</p>
<p>At the end of this course you will be able to:</p>
<ul>
<li>Build a Shiny app in Python,</li>
<li>How to integrate LLMs that can transparently work on your data</li>
<li>Write unit tests and end-to-end tests for your shiny application,</li>
<li>Deploy and share your application (for free!).</li>
</ul>
            </div>

          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://cfp.scipy.org/scipy2025/talk/HEHW8W/" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                SciPy Talk Page
              </a>
              

                
                  <a href="https://github.com/chendaniely/scipy-2025-shiny" class="btn btn-primary btn-sm" target="_blank">
                    <i class="bi bi-file-earmark-slides me-1"></i>
                    View Slides
                  </a>
                  
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://cfp.scipy.org/scipy2025/talk/WM9UFJ/" class="text-decoration-none" target="_blank">
                    Keeping LLMs in Their Lane: Focused AI for Data Science and Research
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="d-flex align-items-start mb-2">
                  <div class="me-2">
                    <i class="bi bi-person-fill"></i>
                  </div>
                  <div class="d-flex flex-column">
                    <span class="fw-bold">Speaker</span>
                    <div>Joe Cheng</div>
                  </div>
                </div>
              </div>

<p>LLMs are powerful, flexible, easy-to-use… and often wrong. This is a dangerous combination, especially for data analysis and scientific research, where correctness and reproducibility are core requirements.</p>
<p>Fortunately, it turns out that by carefully applying LLMs to narrower use cases, we can turn them into surprisingly reliable assistants that accelerate and enhance, rather than undermine, scientific work.</p>
<p>This is not just theory—I’ll showcase working examples of seamlessly integrating LLMs into analytic workflows, helping data scientists build interactive, intelligent applications without needing to be web developers. You’ll see firsthand how keeping LLMs focused lets us leverage their “intelligence” in a way that’s practical, rigorous, and reproducible.</p>
<p>This talk is for Python data scientists, researchers, and developers looking to integrate AI into their work in a practical, responsible way—or skeptical that it’s even possible.</p>
            </div>

          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://cfp.scipy.org/scipy2025/talk/WM9UFJ/" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                SciPy Talk Page
              </a>
              

                
                  <a href="https://github.com/jcheng5/SciPy-2025" class="btn btn-primary btn-sm" target="_blank">
                    <i class="bi bi-file-earmark-slides me-1"></i>
                    View Slides
                  </a>
                  
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://cfp.scipy.org/scipy2025/talk/NRMNDX/" class="text-decoration-none" target="_blank">
                    User guides: engaging new users, delighting old ones
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="d-flex align-items-start mb-2">
                  <div class="me-2">
                    <i class="bi bi-person-fill"></i>
                  </div>
                  <div class="d-flex flex-column">
                    <span class="fw-bold">Speaker</span>
                    <div>Michael Chow</div>
                  </div>
                </div>
              </div>

<p>User guides are the piece you often hit right after clicking the “Learn” or “Get Started” button in a package’s documentation. They’re responsible for onboarding new users, and providing a learning path through a package. Surprisingly, while pieces of documentation like the API Reference tend to be the same, the design of user guides tend to differ across packages.</p>
<p>In this talk, I’ll discuss how to design an effective user guide for open source software. I’ll explain how the guides for Polars, DuckDB, and FastAPI balance working end-to-end like a course, with being browsable like a reference.</p>
            </div>

          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://cfp.scipy.org/scipy2025/talk/NRMNDX/" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                SciPy Talk Page
              </a>
              

                
                  <a href="https://docs.google.com/presentation/d/149UwdljTilauGPm0AXkROm9ArTcMfBfOVxgxaEnjSbQ/" class="btn btn-primary btn-sm" target="_blank">
                    <i class="bi bi-file-earmark-slides me-1"></i>
                    View Slides
                  </a>
                  
          </div>
        </div>
      </div>
    </div>
    
    <div class="g-col-12 g-col-lg-6">
      <div class="card h-100 shadow-sm border-0">
        <div class="card-body">
          <div class="row">
            <div class="col-lg-8">
              <h3 class="card-title text-primary mb-3 mt-0">
                
                  <a href="https://cfp.scipy.org/scipy2025/talk/LNWCSE/" class="text-decoration-none" target="_blank">
                    From One Notebook to Many Reports: Automating with Quarto
                      <i class="bi bi-box-arrow-up-right small"></i>
                  </a>
                  
              </h3>

              <div class="mb-3">
                <div class="d-flex align-items-start mb-2">
                  <div class="me-2">
                    <i class="bi bi-person-fill"></i>
                  </div>
                  <div class="d-flex flex-column">
                    <span class="fw-bold">Speaker</span>
                    <div>Charlotte Wickham</div>
                  </div>
                </div>
              </div>

<p>Would you rather read a “Climate summary” or a “Climate summary for <em>exactly where you live</em>”? Producing documents that tailor your scientific results to an individual or their situation increases understanding, engagement, and connection. But, producing many reports can be onerous.</p>
<p>If you are looking for a way to automate producing many reports, or you produce reports like this but find yourself in copy-and-paste hell, come along to learn how Quarto solves this problem with parameterized reports - you create a single Python notebook, but you generate many beautiful customized PDFs.</p>
            </div>

          </div>
        </div>
        <div class="card-footer" style="background-color: var(--bs-body-bg);">
          <div class="d-flex flex-row gap-2 h-100 justify-content-start">
            
              <a href="https://cfp.scipy.org/scipy2025/talk/LNWCSE/" class="btn btn-outline-primary btn-sm" target="_blank">
                <i class="bi bi-link-45deg me-1"></i>
                SciPy Talk Page
              </a>
              

                
                  <a href="https://cwickham.github.io/one-notebook-many-reports/" class="btn btn-primary btn-sm" target="_blank">
                    <i class="bi bi-file-earmark-slides me-1"></i>
                    View Slides
                  </a>
                  
          </div>
        </div>
      </div>
    </div>
    
</div>

<div class="listing-no-matching d-none">
No matching items
</div>
</div>



</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-at-scipy-2025/</guid>
  <pubDate>Mon, 14 Jul 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-at-scipy-2025/scipy-og.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Shiny 1.4 brings bookmarking and Generative AI docs</title>
  <dc:creator>Shiny Team</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-python-1.4/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-python-1.4/shiny-bookmark.png" alt="Shiny 1.4 brings bookmarking and Generative AI docs" style="width: 100%; height: auto;">
    </div>
    


<style>
  .panel-tabset .tab-content, .nav {
    border: none;
  }
  .panel-tabset.nav-centered .nav {
    justify-content: center;
  }
</style>
<p>We’re pleased to announce that Shiny <code>v1.4</code> is now <a href="https://pypi.org/project/shiny/">available on PyPI</a>! 🎉</p>
<p>Install it now with <code>pip install -U shiny</code>.</p>
<p>To celebrate, let’s highlight two big additions from this release: bookmarking and new Generative AI docs. A full list of changes is available in the <a href="https://github.com/posit-dev/py-shiny/blob/main/CHANGELOG.md#140---2025-04-08">CHANGELOG</a>.</p>
<section id="bookmarking" class="level2">
<h2 data-anchor-id="bookmarking">Bookmarking 🔖</h2>
<p>Shiny now supports bookmarking! This means you can save the current state of your app and return to it later, or share it with others as a URL. This is a great way to improve user experience, especially when it’s difficult to return to a particular state – which is often true for Generative AI apps.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Bookmarking <code>Chat</code>
</div>
</div>
<div class="callout-body-container callout-body">
<p>Bookmarking is crucial for <a href="https://shiny.posit.co/py/docs/genai-chatbots.html">AI chatbots</a>, where returning to a previous conversation is often desired. <a href="https://shiny.posit.co/py/docs/genai-chatbots.html#bookmark-messages">See here</a> to learn how to add bookmarking to your <code>Chat</code> app.</p>
</div>
</div>
<p>Adding bookmark support to an app generally requires some additional effort. At the very least, Shiny needs to know where and when to save state, and in some more advanced cases, how to save/restore it as well:<sup>1</sup></p>
<p>To help you get started, here’s a basic example of how to add bookmarking to an app. Notice how it:</p>
<ol type="1">
<li>Stores state in the URL (i.e., <code>bookmark_store="url"</code>).
<ul>
<li>Server-side storage is also available.</li>
</ul></li>
<li>Provides a <code>ui.input_bookmark_button()</code> to allow the user to trigger a bookmark.
<ul>
<li>Bookmarks can also be triggered programmatically via <code>session.bookmark()</code>.</li>
</ul></li>
<li>When a bookmark happens, the URL is updated with the current state of the app (via <code>session.bookmark.update_query_string()</code>).
<ul>
<li>Updating the URL is optional, but almost always desirable (alternatively, you could open a modal with the bookmark URL).</li>
</ul></li>
</ol>
<div class="tabset-margin-container"></div><div class="panel-tabset nav-pills nav-centered">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true">Express</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false">Core</a></li></ul>
<div class="tab-content nav-pills nav-centered">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> app_opts, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, render, session, ui</span>
<span id="cb1-2"></span>
<span id="cb1-3">app_opts(bookmark_store<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"url"</span>)</span>
<span id="cb1-4"></span>
<span id="cb1-5"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.sidebar():</span>
<span id="cb1-6">    ui.input_slider(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"slider"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Choose a number"</span>, value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>)</span>
<span id="cb1-7">    ui.input_bookmark_button()</span>
<span id="cb1-8"></span>
<span id="cb1-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb1-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> slider_value():</span>
<span id="cb1-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Slider value: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>slider()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-12"></span>
<span id="cb1-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@session.bookmark.on_bookmarked</span></span>
<span id="cb1-14"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> _(url: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb1-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> session.bookmark.update_query_string(url)</span></code></pre></div>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> starlette.requests <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Request</span>
<span id="cb2-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> App, Inputs, Outputs, Session, render, ui</span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> app_ui(request: Request):</span>
<span id="cb2-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> ui.page_sidebar(</span>
<span id="cb2-6">        ui.sidebar(</span>
<span id="cb2-7">            ui.input_slider(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"slider"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Choose a number"</span>, value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>),</span>
<span id="cb2-8">            ui.input_bookmark_button(),</span>
<span id="cb2-9">        ),</span>
<span id="cb2-10">        ui.output_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"slider_value"</span>),</span>
<span id="cb2-11">    )</span>
<span id="cb2-12"></span>
<span id="cb2-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> server(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>: Inputs, output: Outputs, session: Session):</span>
<span id="cb2-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.text</span></span>
<span id="cb2-15">    <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> slider_value():</span>
<span id="cb2-16">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Slider value: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>slider()<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb2-17"></span>
<span id="cb2-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@session.bookmark.on_bookmarked</span></span>
<span id="cb2-19">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">async</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> _(url: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>):</span>
<span id="cb2-20">        <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">await</span> session.bookmark.update_query_string(url)</span>
<span id="cb2-21"></span>
<span id="cb2-22">app <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> App(app_ui, server, bookmark_store<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"url"</span>)</span></code></pre></div>
</div>
</div>
</div>
<p>More generally, some of the key things to keep in mind when bookmarking:</p>
<ul>
<li><strong>Where to save</strong>: For basic apps, state can be likely encoded entirely within the URL, in which case setting <code>bookmark_store="url"</code> is appropriate. However, if you have state that can’t be JSON-encoded or is too large to fit in a URL, use <code>bookmark_store="server"</code> instead.</li>
<li><strong>When to bookmark</strong>: Since a bookmark is potentially expensive to perform, Shiny won’t automatically bookmark for you every time the state changes. Instead, either the user or you (the developer) will need to trigger a bookmark somehow. If bookmarking is a relatively cheap operation in your case, you might consider programmatically triggering a bookmark every time “important” state changes happen. This could be done by calling <code>session.bookmark()</code> in a <code>reactive.effect</code> that watches the relevant inputs.</li>
<li><strong>How to save/restore</strong>: For basic apps, Shiny can automatically figure out how to save and restore it. However, if you have server-side state that can’t be fully determined by the UI’s input values alone, you’ll need to register <code>on_bookmark</code> and <code>on_restore</code> callbacks to save and restore that server-state. For brevity sake, we won’t dig into this topic here, but you can find more information <a href="https://github.com/posit-dev/py-shiny/tree/main/shiny/bookmark">over here</a>.</li>
</ul>
<section id="coming-soon" class="level3">
<h3 data-anchor-id="coming-soon">Coming soon</h3>
<p>Over the coming weeks, be on the lookout for:</p>
<ul>
<li>More bookmarking documentation
<ul>
<li>In the meantime, refer to <a href="https://github.com/posit-dev/py-shiny/tree/main/shiny/bookmark">this README</a> and <a href="https://shiny.posit.co/py/api/core/bookmark.Bookmark.html">the API reference</a>.</li>
</ul></li>
<li>Bookmarking integration into various platforms such as <a href="https://shinylive.io/">shinylive</a>, <a href="https://posit.co/products/enterprise/connect/">Posit Connect</a>, and <a href="https://connect.posit.cloud/">Posit Connect Cloud</a>.</li>
</ul>
</section>
</section>
<section id="genai-docs" class="level2 page-columns page-full">
<h2 data-anchor-id="genai-docs">Generative AI docs 🤖</h2>
<p>For a while now, Shiny has had both a <code>Chat()</code> and <code>MarkdownStream()</code> component for building chatbots and other Generative AI applications. While this release includes improvements and fixes for these components, the real highlight is the new documentation that accompanies them. This includes both new articles as well as templates.</p>
<section id="articles" class="level3 page-columns page-full">
<h3 data-anchor-id="articles">New articles</h3>
<p>Now, when you visit the “Learn Shiny” portion of the documentation, you’ll be met with a new section dedicated to Generative AI in the sidebar. Here’s a screenshot of the new <a href="https://shiny.posit.co/py/docs/genai-chatbots.html">getting started with chatbots</a> page:</p>
<div class="py-3 my-3 page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="genai-docs.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-1" data-glightbox="description: .lightbox-desc-1" title="A screenshot of the new Generative AI docs"><img src="https://shiny.posit.co/blog/posts/shiny-python-1.4/genai-docs.png" class="shadow rounded img-fluid figure-img column-page-inset" alt="A screenshot of the new Generative AI docs"></a></p>
<figcaption>A screenshot of the <a href="https://shiny.posit.co/py/docs/genai-inspiration.html">new Generative AI docs</a></figcaption>
</figure>
</div>
</div>
<p>In addition to an article dedicated to chatbots, which there’s also a dedicated <a href="https://shiny.posit.co/py/docs/genai-stream.html">article on streaming</a>, which is all about <code>MarkdownStream()</code>. Think of this as your go-to component if you want to leverage generative AI, but don’t need a full chat interface. For example, maybe you want to create an experience like the <a href="https://shiny.posit.co/py/templates/workout-plan/">workout plan generator template</a> where a set of input controls are used to user information and fill in a prompt.</p>
</section>
<section id="templates" class="level3 page-columns page-full">
<h3 data-anchor-id="templates">New templates</h3>
<p>We’ve also added a new “Generative AI” section to the <a href="https://shiny.posit.co/py/templates/">templates page</a>. These provide a great starting point for building your own Generative AI application.</p>
<div class="py-3 my-3 page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><a href="genai-templates.png" class="lightbox page-columns page-full" data-gallery="quarto-lightbox-gallery-2" data-glightbox="description: .lightbox-desc-2" title="A screenshot of the new Generative AI templates"><img src="https://shiny.posit.co/blog/posts/shiny-python-1.4/genai-templates.png" class="shadow rounded img-fluid figure-img column-page-inset" alt="A screenshot of the new Generative AI templates"></a></p>
<figcaption>A screenshot of the <a href="https://shiny.posit.co/py/templates/">new Generative AI templates</a></figcaption>
</figure>
</div>
</div>
</section>
<section id="coming-to-r-soon" class="level3">
<h3 data-anchor-id="coming-to-r-soon">Coming to R soon</h3>
<p>Be on the lookout for similar articles and templates making their way to <a href="https://posit-dev.github.io/shinychat/"><code>shinychat</code>’s website</a> soon (i.e., the R equivalent of <code>Chat</code>).</p>
</section>
</section>
<section id="in-closing" class="level2">
<h2 data-anchor-id="in-closing">In closing</h2>
<p>We’re thrilled to bring you these new features and improvements in Shiny <code>v1.4</code>. As always, if you have any questions or feedback, please <a href="https://discord.gg/yMGCamUMnS">join us on Discord</a> or <a href="https://github.com/posit-dev/py-shiny/issues/new">open an issue on GitHub</a>. Happy Shiny-ing!</p>



</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>When server-side state can’t be fully determined by the UI’s input values alone, you’ll need to register <code>on_bookmark</code> and <code>on_restore</code> callbacks to save and restore that server-state.↩︎</p></li>
</ol>
</section></div> ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-python-1.4/</guid>
  <pubDate>Tue, 15 Apr 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-python-1.4/shiny-bookmark.png" medium="image" type="image/png" height="96" width="144"/>
</item>
<item>
  <title>chromote v0.5.0</title>
  <dc:creator>Garrick Aden-Buie</dc:creator>
  <link>https://shiny.posit.co/blog/posts/chromote-0.5.0/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/chromote-0.5.0/feature.png" alt="chromote v0.5.0" style="width: 100%; height: auto;">
    </div>
    


<div class="lead">
<p>We are excited to announce the latest release of <a href="https://rstudio.github.io/chromote">chromote</a> v0.5.0, which brings new functions that make it much easier to download and use any version of Chrome.</p>
</div>
<section id="getting-started" class="level2">
<h2 data-anchor-id="getting-started">Getting Started</h2>
<p><a href="https://rstudio.github.io/chromote">chromote</a> lets you drive and access the Chrome web browser<sup>1</sup> programmatically from R using Chrome’s headless mode, making it useful for tasks ranging from screenshots and web scraping to full automated browser testing. chromote is a powerful, but low-level, package that powers other easier-to-use packages:</p>
<ul>
<li>Take screenshots of web pages with <a href="https://rstudio.github.io/webshot2">webshot2</a></li>
<li>Test Shiny apps with <a href="https://rstudio.github.io/shinytest2">shinytest2</a></li>
<li>Scrape otherwise hard-to-access content from websites with <code>rvest::read_html_live()</code></li>
</ul>
<p>To get started, make sure you’ve installed the latest version of <a href="https://rstudio.github.io/chromote">chromote</a>:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chromote"</span>)</span></code></pre></div>
</section>
<section id="what-is-headless-mode" class="level2">
<h2 data-anchor-id="what-is-headless-mode">What is headless mode?</h2>
<p>Chrome’s headless mode is a special browsing mode without a visible interface, which is ideal for automated testing and server environments. In headless mode, developers can run and drive Chrome remotely<sup>2</sup>, without the overhead of having to click, type, and interact with the browser manually.</p>
<p>You could think of headless mode as using Chrome on a computer without a monitor. This analogy is now mostly correct, but it wasn’t always this way.</p>
<p>Initially, headless mode wasn’t just Chrome without a UI; it was a entirely separate browser designed for programmatic use, independent of the standard Chrome browser. Although differences between headless and regular Chrome were small, they mattered for situations in which consistency with the user experience is crucial.</p>
</section>
<section id="a-year-of-big-changes" class="level2 page-columns page-full">
<h2 data-anchor-id="a-year-of-big-changes">A year of big changes</h2>
<div class="column-body-outset">
<div class="cell" data-layout-align="default">
<div class="cell-output-display">
<div id="timeline">
<div>
<pre class="mermaid mermaid-js" data-label="timeline">%% Custom CSS to style the chart
%%{init: {
    'themeVariables': {
        'gridLineColor': '#e0e0e0',
        'todayLineColor': '#00000000'
    }
}}%%

gantt
    title Chrome Headless Mode Timeline
    dateFormat YYYY-MM
    axisFormat %b %Y
    
    section Chrome
    v112 (New headless introduced)    :milestone, m1, 2023-04, 0d
    v128 (New headless default)       :milestone, m2, 2024-08, 0d
    v132 (Old headless removed)       :milestone, m3, 2025-01, 0d
    
    section Headless Modes
    Old Headless Default    :active, h1, 2023-03, 2024-08
    New Headless Optional   :done, h2, 2023-03, 2024-08
    Old Headless Optional   :done, h4, 2024-08, 2025-01
    New Headless Default    :active, h3, 2024-08, 2025-01
    New Headless Only      :active, h5, 2025-01, 2025-03
    
    section chromote
    v0.3.1        :milestone, c1, 2024-08, 0d
    v0.4.0        :milestone, c2, 2025-01, 0d

</pre>
</div>
</div>
</div>
</div>
</div>
<style>
#timeline .grid .tick {
  stroke: lightgrey;
  opacity: 0.3;
  shape-rendering: crispEdges;
}
#timeline .grid path {
  stroke-width: 0;
}
</style>
<p>When Chrome released version 112 in early 2023, they announced a <a href="https://developer.chrome.com/docs/chromium/headless">new, unified headless mode</a> that uses the same browser engine as regular, headful Chrome, ensuring consistency in automated testing. chromote automatically launches Chrome for you, activating headless mode by including the <code>--headless</code> flag in the process. Old headless mode remained the default <code>--headless</code> mode, but you’d have to opt into this mode by using <code>--headless=new</code> when launching Chrome, which wasn’t possible at the time with chromote.</p>
<p>Then, with Chrome v128 (August 2024), the meaning of <code>--headless</code> changed from <code>old</code> to <code>new</code>, making <em>new headless mode</em> the default headless version. To minimize disruption to chromote users, we quickly <a href="https://rstudio.github.io/chromote/news/index.html#chromote-031">patched chromote and released v0.3.1</a>, to continue to use the old headless mode with <code>--headless=old</code>, while offering adventurous users a way to try the new headless mode via an R option.</p>
<p>Finally, in <a href="https://developer.chrome.com/blog/removing-headless-old-from-chrome?hl=en">Chrome v132 (January 2025)</a>, old headless mode was completely removed from Chrome and made available as a new, separate binary, <a href="https://developer.chrome.com/blog/chrome-headless-shell">chrome-headless-shell</a>. Again, we <a href="https://rstudio.github.io/chromote/news/index.html#chromote-040">patched chromote and released v0.4.0</a> to use <code>--headless</code> instead of <code>--headless=old</code>, which will now cause an error when used with Chrome v132 and later.</p>
</section>
<section id="which-version-of-chrome-is-best-for-chromote-users" class="level2">
<h2 data-anchor-id="which-version-of-chrome-is-best-for-chromote-users">Which version of chrome is best for chromote users?</h2>
<p>The Chrome developers helpfully shared this diagram comparing old and new headless modes.</p>
<p><img src="https://shiny.posit.co/blog/posts/chromote-0.5.0/old-new-headless.svg" class="img-fluid" alt="A chart comparing Old Headless and New Headless Chrome features. The horizontal axis ranges from &quot;performant&quot; to &quot;authentic,&quot; while the vertical axis ranges from &quot;lightweight&quot; to &quot;fully featured.&quot; Old Headless is positioned in the lower left quadrant, labeled with &quot;screenshotting, PDF printing, and web scraping/crawling.&quot; In contrast, New Headless is in the upper right quadrant, labeled with &quot;reliable web app testing and extension testing.&quot;"></p>
<p>Practically speaking, most chromote users will be happiest using <code>chrome-headless-shell</code>, i.e.&nbsp;the specialized binary that continues the functionality of the old headless mode. As highlighted by the Chrome team, <code>chrome-headless-shell</code> starts up faster and is well-suited to screenshots, printing to PDF and for web scraping—all common use cases for chromote.</p>
</section>
<section id="keeping-chromote-up-to-date" class="level2">
<h2 data-anchor-id="keeping-chromote-up-to-date">Keeping chromote up-to-date</h2>
<p>One of chromote’s greatest design strengths is that it works with practically <em>any</em> version of Chrome. Rather than implement static bindings to specific versions of Chrome in a way that requires manual updates with each new version of Chrome, chromote asks Chrome for the schema of the Chrome DevTools Protocol — the commands supported by headless Chrome — and then it uses this schema to hydrate the package with the exact methods matching the version of Chrome you’re using.</p>
<p>Most Chrome updates work seamlessly with chromote, but the major shift from old to new headless mode — a huge undertaking on a very large codebase — created some inevitable complications for users.</p>
<p>Modern browsers, like Chrome, operate with rolling updates on a regular schedule. This is great for users and internet browsers, but not great for the reproducibility of automated scripts that use chromote. It’s a frustrating experience to have a script break from one day to the next because Chrome updated overnight.</p>
<p>Fortunately, chromote v0.5. includes new features that make it easier to use the exact version of Chrome that you want.</p>
</section>
<section id="managing-chrome-versions-with-chromote-v0.5.0" class="level2">
<h2 data-anchor-id="managing-chrome-versions-with-chromote-v0.5.0">Managing Chrome versions with chromote v0.5.0</h2>
<p>chromote v0.5.0 includes new features that let you download any version of Chrome or <code>chrome-headless-shell</code> from the <a href="https://googlechromelabs.github.io/chrome-for-testing">Google Chrome for Testing service</a>, drawing inspiration from similar features available in the JavaScript and Python browser-testing package <a href="https://playwright.dev/docs/browsers#managing-browser-binaries">playwright</a>.</p>
<p><img src="https://shiny.posit.co/blog/posts/chromote-0.5.0/chromote-demo.svg" class="img-fluid" alt="An animation showing local_chrome_version() in use in the R console. The code in the animation is described in the post text."></p>
<p>To get started, call <a href="https://rstudio.github.io/chromote/reference/with_chrome_version.html"><code>local_chrome_version()</code></a> with a specific <code>version</code> and <code>binary</code> choice at the start of your script, before you create a new <code>ChromoteSession</code> or use another package that relies on chromote.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(chromote)</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">local_chrome_version</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"latest-stable"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">binary =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chrome"</span>)</span>
<span id="cb2-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ℹ Downloading `chrome` version 134.0.6998.88 for mac-arm64</span></span>
<span id="cb2-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; trying URL 'https://storage.googleapis.com/chrome-for-testing-public/134.0.6998.88/mac-arm64/chrome-mac-arm64.zip'</span></span>
<span id="cb2-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Content type 'application/zip' length 158060459 bytes (150.7 MB)</span></span>
<span id="cb2-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ==================================================</span></span>
<span id="cb2-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; downloaded 150.7 MB</span></span>
<span id="cb2-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb2-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ✔ Downloading `chrome` version 134.0.6998.88 for mac-arm64 [5.3s]</span></span>
<span id="cb2-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; chromote will now use version 134.0.6998.88 of `chrome` for mac-arm64.</span></span>
<span id="cb2-12"></span>
<span id="cb2-13">b <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ChromoteSession<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>()</span></code></pre></div>
<p>By default, <code>local_chrome_version()</code> uses the latest stable version of Chrome, matching the arguments shown in the code example above.</p>
<p>For scripts with a longer life span and to ensure reproducibility, you can choose a specific version of Chrome or <code>chrome-headless-shell</code>:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">local_chrome_version</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"134.0.6998.88"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">binary =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chrome-headless-shell"</span>)</span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; chromote will now use version 134.0.6998.88 of `chrome-headless-shell` for mac-arm64.</span></span></code></pre></div>
<p>If you don’t already have a copy of the requested version of the binary, <code>local_chrome_version()</code> will download it for you so you’ll only need to download the binary once. You can list all of the versions and binaries you’ve installed with <code>chrome_versions_list()</code>, or all available versions and binaries with <code>chrome_versions_list("all")</code>.</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chrome_versions_list</span>()</span>
<span id="cb4-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; # A tibble: 2 × 6</span></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   version       revision binary                platform  url                        path </span></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   &lt;chr&gt;         &lt;chr&gt;    &lt;chr&gt;                 &lt;chr&gt;     &lt;chr&gt;                      &lt;chr&gt;</span></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1 134.0.6998.88 1415337  chrome                mac-arm64 https://storage.googleapi… /Use…</span></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 2 134.0.6998.88 1415337  chrome-headless-shell mac-arm64 https://storage.googleapi… /Use…</span></span></code></pre></div>
<p><code>local_chrome_version()</code> sets the version of Chrome for the current session or within the context of a function. For small tasks where you want to use a specific version of Chrome for a few lines of code, chromote provides a <code>with_chrome_version()</code> variant:</p>
<div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with_chrome_version</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"132"</span>, {</span>
<span id="cb5-2">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Take a screenshot with Chrome v132</span></span>
<span id="cb5-3">  webshot2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">webshot</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://r-project.org"</span>)</span>
<span id="cb5-4">})</span></code></pre></div>
<p>Finally, you can manage Chrome binaries directly with three additional helper functions:</p>
<ol type="1">
<li><p><code>chrome_versions_add()</code> can be used to add a new Chrome version to the cache, without explicitly configuring chromote to use that version.</p>
<div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chrome_versions_add</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">135</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chrome-headless-shell"</span>)</span>
<span id="cb6-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ℹ Downloading `chrome-headless-shell` version 135.0.7049.17 for mac-arm64</span></span>
<span id="cb6-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; trying URL 'https://storage.googleapis.com/chrome-for-testing-public/135.0.7049.17/mac-arm64/chrome-headless-shell-mac-arm64.zip'</span></span>
<span id="cb6-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Content type 'application/zip' length 90601586 bytes (86.4 MB)</span></span>
<span id="cb6-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ==================================================</span></span>
<span id="cb6-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; downloaded 86.4 MB</span></span>
<span id="cb6-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb6-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ✔ Downloading `chrome-headless-shell` version 135.0.7049.17 for mac-arm64 [3.2s]</span></span>
<span id="cb6-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "/Users/garrick/Library/Caches/org.R-project.R/R/chromote/chrome/135.0.7049.17/chrome-headless-shell-mac-arm64/chrome-headless-shell"</span></span></code></pre></div></li>
<li><p><code>chrome_versions_path()</code> returns the path to the Chrome binary for a given version and binary type.</p>
<div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chrome_versions_path</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">135</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chrome-headless-shell"</span>)</span>
<span id="cb7-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "/Users/garrick/Library/Caches/org.R-project.R/R/chromote/chrome/135.0.7049.17/chrome-headless-shell-mac-arm64/chrome-headless-shell"</span></span></code></pre></div></li>
<li><p><code>chrome_versions_remove()</code> can be used to delete copies of Chrome from the local cache.</p>
<div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chrome_versions_remove</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">135</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chrome-headless-shell"</span>)</span>
<span id="cb8-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Will remove 1 cached version of chrome:</span></span>
<span id="cb8-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; /Users/garrick/Library/Caches/org.R-project.R/R/chromote/chrome/135.0.7049.17/chrome-headless-shell-mac-arm64</span></span>
<span id="cb8-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Delete from cache?</span></span>
<span id="cb8-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb8-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1: Yes</span></span>
<span id="cb8-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 2: No</span></span>
<span id="cb8-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 3: Cancel</span></span>
<span id="cb8-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb8-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Selection: 1</span></span></code></pre></div></li>
</ol>
</section>
<section id="other-new-features-in-chromote-v0.5.0" class="level2">
<h2 data-anchor-id="other-new-features-in-chromote-v0.5.0">Other new features in chromote v0.5.0</h2>
<p><img src="https://shiny.posit.co/blog/posts/chromote-0.5.0/chromote.png" class="float-sm-end m-auto m-sm-3 d-block img-fluid" style="max-width: min(33%, 180px)" alt="The chromote logo, featuring a minimalist design inspired by the Chrome browser logo. The text 'chromote' appears in white lowercase letters above a white circular ring reminiscent of Chrome's central element. The entire design is set within a hexagonal shape composed of geometric sections in varying shades of blue, from deep navy to lighter blue."></p>
<p>chromote now has a hex sticker! Sorry for burying the lede, we know this is probably the most exciting update in this release. Thank you to <a href="https://github.com/davidrsch">David Díaz Rodríguez</a> for proposing a design that inspired the final hex sticker.</p>
<p>One more interesting feature from this release is a new <code>$set_viewport_size()</code> method that makes it easier to adjust the virtual window size of a chromote tab:</p>
<div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1">b <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> ChromoteSession<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new</span>()</span>
<span id="cb9-2"></span>
<span id="cb9-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Common laptop resolution</span></span>
<span id="cb9-4">b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set_viewport_size</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1366</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">768</span>)</span>
<span id="cb9-5"></span>
<span id="cb9-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># iPhone 13, with mobile device emulation</span></span>
<span id="cb9-7">b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set_viewport_size</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">390</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">844</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mobile =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span></code></pre></div>
<p>You can learn more in the <a href="https://rstudio.github.io/chromote/reference/ChromoteSession.html#method-set-viewport-size-"><code>$set_viewport_size()</code> method documentation</a>.</p>
</section>
<div class="column-screen bg-light mt-4 page-columns page-full">
<section id="release-notes" class="level2 column-body">
<h2 data-anchor-id="release-notes">Thank you 💙</h2>
<p>This post covered the biggest changes to chromote in this release, but there’s even more in the <a href="https://rstudio.github.io/chromote/news/index.html#chromote-050">chromote v0.5.0 release notes</a>!</p>
<p><strong>A huge thank you</strong> to everyone who contributed pull requests, bug reports and feature requests.</p>
<p><a href="https://github.com/aaelony-fb">@aaelony-fb</a>, <a href="https://github.com/amurtha80">@amurtha80</a>, <a href="https://github.com/ashbythorpe">@ashbythorpe</a>, <a href="https://github.com/bradvf">@bradvf</a>, <a href="https://github.com/braverock">@braverock</a>, <a href="https://github.com/cellocgw">@cellocgw</a>, <a href="https://github.com/chlebowa">@chlebowa</a>, <a href="https://github.com/daryabusen">@daryabusen</a>, <a href="https://github.com/davidrsch">@davidrsch</a>, <a href="https://github.com/DivadNojnarg">@DivadNojnarg</a>, <a href="https://github.com/fh-mthomson">@fh-mthomson</a>, <a href="https://github.com/gadenbuie">@gadenbuie</a>, <a href="https://github.com/ISumaneev">@ISumaneev</a>, <a href="https://github.com/maelle">@maelle</a>, <a href="https://github.com/Manestricker">@Manestricker</a>, <a href="https://github.com/marvin3FF">@marvin3FF</a>, <a href="https://github.com/nclsbarreto">@nclsbarreto</a>, <a href="https://github.com/oozbeker-onemagnify">@oozbeker-onemagnify</a>, <a href="https://github.com/PaulC91">@PaulC91</a>, <a href="https://github.com/r2evans">@r2evans</a>, <a href="https://github.com/rcepka">@rcepka</a>, <a href="https://github.com/sale4cast">@sale4cast</a>, <a href="https://github.com/saleforecast1">@saleforecast1</a>, <a href="https://github.com/schloerke">@schloerke</a>, <a href="https://github.com/simon-smart88">@simon-smart88</a>, <a href="https://github.com/skyeturriff">@skyeturriff</a>, <a href="https://github.com/stadbern">@stadbern</a>, <a href="https://github.com/sybrohee">@sybrohee</a>, and <a href="https://github.com/zeloff">@zeloff</a>.</p>
</section>
</div>




<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Or any browser based on <a href="https://www.google.com/chrome/index.html">Chrome</a> or <a href="https://www.chromium.org/chromium-projects/">chromium</a>, of which <a href="https://en.wikipedia.org/wiki/Chromium_(web_browser)#Browsers_based_on_Chromium">there are many</a>.↩︎</p></li>
<li id="fn2"><p>Run chrome remotely: <em>chromote</em>.↩︎</p></li>
</ol>
</section></div> ]]></description>
  <guid>https://shiny.posit.co/blog/posts/chromote-0.5.0/</guid>
  <pubDate>Fri, 21 Mar 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/chromote-0.5.0/feature.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Creating Responsive Layouts in Shiny with layout_columns()</title>
  <dc:creator>Shiny Team</dc:creator>
  <link>https://shiny.posit.co/blog/posts/responsive-shiny-layouts/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/responsive-shiny-layouts/shiny-layouts.jpg" style="width: 100%; height: auto;">
    </div>
    


<section id="shiny-layouts-for-different-screen-sizes" class="level2 page-columns page-full">
<h2 data-anchor-id="shiny-layouts-for-different-screen-sizes">Shiny Layouts for Different Screen Sizes</h2>
<p>Creating web applications that look good on any device is crucial. Shiny offers tools to make your dashboards responsive. In this post, we’ll explore how to use <code>layout_columns()</code> to tailor your app’s layout for desktops, tablets, and smartphones.</p>
<section id="the-problem-default-layout-behavior" class="level3">
<h3 data-anchor-id="the-problem-default-layout-behavior">The Problem: Default Layout Behavior</h3>
<p>When first starting with Shiny, developers might create UI elements without explicitly defining columns. Shiny’s default behavior is responsive in that it will stretch elements to fill the available space. However, this can lead to layouts that look great on a large monitor but become squashed or unwieldy on smaller screens.</p>
</section>
<section id="the-solution-ui.layout_columns" class="level3">
<h3 data-anchor-id="the-solution-ui.layout_columns">The Solution: ui.layout_columns()</h3>
<p><code>ui.layout_columns()</code> is a powerful function that lets you define column-based layouts that adjust based on screen width. It leverages the Bootstrap grid system, dividing the screen into 12 virtual columns. You can then specify how many of these columns each element should occupy at different breakpoints.</p>
</section>
<section id="key-concepts" class="level3">
<h3 data-anchor-id="key-concepts">Key Concepts</h3>
<ul>
<li><strong>Breakpoints:</strong> These define the screen widths at which the layout changes. Shiny provides common breakpoints:
<ul>
<li><code>lg</code>: Large (desktop, typically 992px and wider)</li>
<li><code>md</code>: Medium (tablets, typically 768px and wider)</li>
<li><code>sm</code>: Small (smartphones)</li>
<li><code>xs</code>: Extra Small (smaller smartphones)</li>
</ul></li>
<li><strong><code>col_widths</code>:</strong> This argument within <code>ui.layout_columns()</code> is where the magic happens. You provide a list or dictionary that specifies the number of columns for each UI element at each breakpoint.</li>
</ul>
</section>
<section id="example-two-column-layout-on-desktop-single-column-on-mobile" class="level3 page-columns page-full">
<h3 data-anchor-id="example-two-column-layout-on-desktop-single-column-on-mobile">Example: Two-Column Layout on Desktop, Single-Column on Mobile</h3>
<p>Let’s say we want two cards to appear side-by-side on a desktop, but stacked vertically on a mobile device. Here’s how we can achieve that using Shiny Express mode (the same principle applies to core Shiny):</p>
<div class="column-page py-4">
<div class="quarto-video ratio ratio-16x9"><iframe data-external="1" src="https://www.youtube.com/embed/KkUpgeUIVvM" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
</div>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.layout_columns(</span>
<span id="cb1-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Responsive widths</span></span>
<span id="cb1-3">    col_widths<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb1-4">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lg"</span>: (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>), <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Large screens</span></span>
<span id="cb1-5">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"md"</span>: (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>), <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Medium screens</span></span>
<span id="cb1-6">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sm"</span>: (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>), <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Small screens</span></span>
<span id="cb1-7">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"xs"</span>: (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>), <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Extra small screens</span></span>
<span id="cb1-8">    },</span>
<span id="cb1-9">    card1</span>
<span id="cb1-10">    card2</span>
<span id="cb1-11">    card3</span></code></pre></div>
<p><strong>Explanation:</strong></p>
<ul>
<li><p><code>ui.layout_columns()</code>: We use this function to wrap our three cards.</p></li>
<li><p><code>col_widths</code>:</p>
<ul>
<li><code>"lg": (6, 6, 12)</code>: On large screens, the first two cards each take up 6 out of 12 columns (half the width), and the third card takes up all 12 columns (full width). This positions the first two cards side-by-side and the third card below them.</li>
<li><code>"md": (12, 12, 12)</code>, <code>"sm": (12, 12, 12)</code>, <code>"xs": (12, 12, 12)</code>: On medium, small, and extra-small screens, <em>all</em> cards take up the full 12 columns. This stacks them vertically.</li>
</ul></li>
</ul>
</section>
<section id="going-further" class="level3">
<h3 data-anchor-id="going-further">Going Further</h3>
<p>The <code>ui.layout_columns()</code> function is quite flexible. You can:</p>
<ul>
<li>Use a list instead of a dictionary for <code>col_widths</code> if you want the same layout for all breakpoints.</li>
<li>Use a dictionary of column widths at different breakpoints. The keys should be one of “xs”, “sm”, “md”, “lg”, or “xl”, or “xxl”, and the values are either of the above. For example, <code>col_widths={"lg": (4, 8, 12)}</code></li>
</ul>
<p>This technique allows you to create sophisticated, responsive layouts that adapt gracefully to different screen sizes, providing an optimal user experience across all devices. Check out the <a href="https://shiny.posit.co/py/api/express.ui.layout_columns.html">Shiny documentation</a> for more details and advanced options.</p>


</section>
</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/responsive-shiny-layouts/</guid>
  <pubDate>Sat, 08 Feb 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/responsive-shiny-layouts/shiny-layouts.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Level Up Your Shiny Forms: Accordions + Dynamic Goodness</title>
  <dc:creator>Shiny Team</dc:creator>
  <link>https://shiny.posit.co/blog/posts/dynamic-accordion-panels/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/dynamic-accordion-panels/forms.jpg" style="width: 100%; height: auto;">
    </div>
    


<p>Shiny for Python makes building web apps easy. This framework simplifies creating interactive applications with Python—no need for JavaScript or HTML. Its secret lies in reactive programming, which keeps your UI dynamic with minimal effort. Just focus on your Python code, and let Shiny take care of the rest!</p>
<div class="callout callout-style-default callout-note callout-titled" title="Install Shiny for Python">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Install Shiny for Python
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>To install the Shiny for Python package, users can use the following command in their terminal or command prompt:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shiny&gt;=1.0"</span></span></code></pre></div>
</div>
</div>
</div>
<p>Basic Shiny forms can feel… well, basic. But what if you’ve got a form with a lot of info? Accordion panels to the rescue! They organize things nicely, but we can make them even cooler with dynamic updates.</p>
<section id="accordions-form-organization-made-easy" class="level3">
<h3 data-anchor-id="accordions-form-organization-made-easy">Accordions: Form Organization made easy</h3>
<p>Instead of one long form, think sections! Accordions let you group related inputs and collapse them. Cleaner, right?</p>
<p><strong>Why Accordions are Awesome:</strong></p>
<ul>
<li><strong>Neat &amp; Tidy:</strong> Sections keep things organized.</li>
<li><strong>Less Overwhelming:</strong> Users focus on one part at a time.</li>
<li><strong>Easy Navigation:</strong> Clear section titles guide the way.</li>
</ul>
<section id="forms-without-and-with-the-use-of-accordions" class="level4">
<h4 data-anchor-id="forms-without-and-with-the-use-of-accordions"><strong>Forms without and with the use of Accordions</strong></h4>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Basic form</span></span>
<span id="cb2-2">ui.h2(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Basic form"</span>)</span>
<span id="cb2-3"></span>
<span id="cb2-4">ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"name"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Name"</span>)</span>
<span id="cb2-5">ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"email"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Email"</span>)</span>
<span id="cb2-6">ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"phone"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Phone"</span>)</span>
<span id="cb2-7">ui.input_action_button(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit_personal"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Submit"</span>)</span></code></pre></div>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Accordion form</span></span>
<span id="cb3-2"><span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.accordion(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"info_accordion"</span>, multiple<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb3-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">with</span> ui.accordion_panel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Personal Information"</span>, value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"personal"</span>):</span>
<span id="cb3-4">        ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"name"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Name"</span>)</span>
<span id="cb3-5">        ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"email"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Email"</span>)</span>
<span id="cb3-6">        ui.input_text(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"phone"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Phone"</span>)</span>
<span id="cb3-7">        ui.input_action_button(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"submit_personal"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Submit"</span>)</span></code></pre></div>
<p><img src="https://shiny.posit.co/blog/posts/dynamic-accordion-panels/comparison.png" class="img-fluid"></p>
</section>
</section>
<section id="making-accordions-dynamic" class="level3 page-columns page-full">
<h3 data-anchor-id="making-accordions-dynamic">Making Accordions dynamic</h3>
<p>The video below demonstrates a powerful technique to further enhance accordion forms through <strong>dynamic updates</strong>. This involves programmatically modifying the appearance of accordion panels in response to user actions, creating a more interactive and informative interface.</p>
<div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.effect</span></span>
<span id="cb4-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@reactive.event</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.submit_personal)</span>
<span id="cb4-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> update_accordion_1():</span>
<span id="cb4-4">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get input values, defaulting to "N/A" if empty</span></span>
<span id="cb4-5">    name <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.name() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"N/A"</span></span>
<span id="cb4-6">    email <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.email() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"N/A"</span></span>
<span id="cb4-7">    phone <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>.phone() <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"N/A"</span></span>
<span id="cb4-8"></span>
<span id="cb4-9">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create new title string with user input values</span></span>
<span id="cb4-10">    new_title <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Name: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>name<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, Email: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>email<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, Phone: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>phone<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb4-11"></span>
<span id="cb4-12">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Updates the appearance and state of an accordion panel dynamically</span></span>
<span id="cb4-13">    ui.update_accordion_panel(</span>
<span id="cb4-14">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"info_accordion"</span>,</span>
<span id="cb4-15">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"personal"</span>,</span>
<span id="cb4-16">        title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>new_title,     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set new title with user info</span></span>
<span id="cb4-17">        show<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Collapse this panel</span></span>
<span id="cb4-18">        icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>icon_svg(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"check"</span>, fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"green"</span>),  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Show green checkmark icon</span></span>
<span id="cb4-19">    )</span>
<span id="cb4-20">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Open the next panel automatically</span></span>
<span id="cb4-21">    ui.update_accordion_panel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"info_accordion"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"professional"</span>, show<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span></code></pre></div>
<div class="column-page py-4">
<div class="quarto-video ratio ratio-16x9"><iframe data-external="1" src="https://www.youtube.com/embed/v26E2_1cSa8" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
</div>
<p>Specifically, the video showcases how to:</p>
<ol type="1">
<li><p><strong>Dynamically Change Panel Titles:</strong> Upon completing a section of the form and clicking a <em>“Submit”</em> button, the accordion panel title is updated to reflect the entered information. This provides immediate visual confirmation and a clear summary of the data provided within each section.</p></li>
<li><p><strong>Incorporate Dynamic Icons for Status Indication:</strong> Beyond textual updates, the example effectively uses icons to visually communicate the status of each accordion panel. Initially, an exclamation icon in red may indicate sections requiring user attention. Upon successful submission of a section, this icon can be dynamically updated to a green checkmark. This visual cue provides instant feedback, allowing users to easily identify completed sections and those still requiring input.</p></li>
</ol>
<p>The implementation leverages the <a href="https://shiny.posit.co/py/api/express/express.ui.update_accordion_panel.html">ui.update_accordion_panel</a> function (in the context of Shiny for Python’s Express mode), which allows developers to programmatically modify various aspects of an existing accordion panel, including its title and icon.</p>
<p>Moving beyond basic form structures is crucial for creating sophisticated and user-centric Shiny applications. <strong>Accordion panels</strong> offer a significant step forward in form organization and user experience. By incorporating dynamic updates, as demonstrated in the video, developers can further elevate their Shiny forms, creating interfaces that are not only functional but also visually informative and engaging. This approach contributes to a more polished and professional application, ultimately enhancing user satisfaction and data interaction efficiency.</p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/dynamic-accordion-panels/</guid>
  <pubDate>Wed, 05 Feb 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/dynamic-accordion-panels/forms.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>bslib v0.9.0 brings branded theming to Shiny for R</title>
  <dc:creator>Garrick Aden-Buie</dc:creator>
  <link>https://shiny.posit.co/blog/posts/bslib-0.9.0/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/bslib-0.9.0/feature.jpg" alt="bslib + brand.yml" style="width: 100%; height: auto;">
    </div>
    


<div class="lead">
<p>We are excited to share that with <a href="https://rstudio.github.io/bslib">bslib</a> v0.9.0, Shiny for R now supports <a href="https://posit-dev.github.io/brand-yml">brand.yml</a>, providing a simple and unified theming experience through a single YAML file!</p>
</div>
<section id="what-is-brand.yml" class="level2">
<h2 data-anchor-id="what-is-brand.yml">What is brand.yml?</h2>
<p><a href="https://posit-dev.github.io/brand-yml">brand.yml</a> simplifies brand management by consolidating your visual identity—colors, typography, and styling—into a single, easy-to-maintain YAML file. Last year, we launched brand.yml with initial support in <a href="https://quarto.org/docs/authoring/brand.html">Quarto</a> and <a href="../../../blog/posts/shiny-python-1.2-brand-yml/index.html">Shiny for Python</a>, and we’re happy to be bringing brand.yml to R as well.</p>
<p>If you haven’t seen <a href="https://posit-dev.github.io/brand-yml">brand.yml</a> in action yet, here’s an example <code>_brand.yml</code> file that includes metadata about the company, its logos, color palette, theme, and the fonts and typographic settings used by the brand.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_brand.yml</strong></pre>
</div>
<div class="sourceCode" id="cb1" data-filename="_brand.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">meta</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand.yml</span></span>
<span id="cb1-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">link</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> https://posit-dev.github.io/brand-yml</span></span>
<span id="cb1-4"></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">logo</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span></span>
<span id="cb1-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">small</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand-yml-icon.svg</span></span>
<span id="cb1-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">medium</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand-yml-tall.svg</span></span>
<span id="cb1-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">large</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand-yml-wide.svg</span></span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">color</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">palette</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">orange</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#FF6F20"</span></span>
<span id="cb1-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pink</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#FF3D7F"</span></span>
<span id="cb1-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">primary</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> orange</span></span>
<span id="cb1-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">danger</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pink</span></span>
<span id="cb1-16"></span>
<span id="cb1-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typography</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fonts</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">family</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Open Sans</span></span>
<span id="cb1-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> google</span></span>
<span id="cb1-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">base</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Open Sans</span></span></code></pre></div>
</div>
<p>This one file can be used to maintain consistent branding across your Shiny apps, Quarto projects, and now any R-based project that uses <a href="https://rstudio.github.io/bslib/articles/theming/index.html">bslib for theming</a>, including <a href="https://shiny.posit.co">shiny</a>, <a href="https://rmarkdown.rstudio.com">R Markdown</a>, <a href="https://pkgdown.r-lib.org/">pkgdown</a>, <a href="https://pkgs.rstudio.com/flexdashboard/">flexdashboard</a>, and more!</p>
</section>
<section id="getting-started" class="level2 page-columns page-full">
<h2 data-anchor-id="getting-started">Getting Started</h2>
<p>bslib is a package maintained by the Shiny team to provide [Bootstrap] styles and components for the R ecosystem. With bslib, using brand.yml to theme your Shiny app is straightforward. To get started, make sure you’ve installed the latest version of bslib:</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bslib"</span>)</span></code></pre></div>
<p>Then, create a <code>_brand.yml</code> file in your project directory, either alongside your <code>app.R</code> or in a parent folder of the project containing your app.</p>
<p>If your app uses any of the <a href="https://rstudio.github.io/bslib/reference/index.html#page-layouts">page functions from bslib</a> like <code>page_sidebar()</code> or <code>page_navbar()</code>, then you’re all set! bslib will automatically find the <code>_brand.yml</code> file and apply it to the app’s theme. For page functions from Shiny—like <code>fluidPage()</code> or <code>navbarPage()</code>—set <code>theme = bs_theme()</code> in the page function.</p>
<p>If you want to use another file name, like <code>acme-brand.yml</code>, or you want to be explicit, you can call <code>bs_theme()</code> and provide the path to the brand.yml file to the <code>brand</code> argument:</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">ui <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">page_sidebar</span>(</span>
<span id="cb3-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Acme Sales Dashboard"</span>,</span>
<span id="cb3-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">theme =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bs_theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">brand =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"acme-brand.yml"</span>),</span>
<span id="cb3-4">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ... the rest of your app ...</span></span>
<span id="cb3-5">)</span></code></pre></div>
</div>
<div class="column-screen bg-gradient-blue-teal mt-4 pt-5 pb-4 d-md-flex text-center">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/bslib-0.9.0/shiny-no-branding.png" class="img-fluid figure-img"></p>
<figcaption><span class="text-white">Shiny without branding</span></figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/bslib-0.9.0/shiny-branding.png" class="img-fluid figure-img"></p>
<figcaption><span class="text-white">Brand-themed Shiny</span></figcaption>
</figure>
</div>
</div>
</section>
<section id="try-brand.yml-in-shiny-now" class="level2">
<h2 data-anchor-id="try-brand.yml-in-shiny-now">Try brand.yml in Shiny now!</h2>
<p>Try brand.yml now with the <a href="https://bslib.shinyapps.io/brand-yml/">brand.yml Demo App</a>. You can create and preview your own brand.yml files with this app, and it’s included with bslib so that you can try it locally, too.</p>
<div class="cell">
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># requires shiny v1.8.1 or later</span></span>
<span id="cb4-2">shiny<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runExample</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand.yml"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">package =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bslib"</span>)</span></code></pre></div>
</div>
<p>This app can also be used as a template; see <a href="https://rstudio.github.io/bslib/articles/brand-yml/index.html#try-brand-yml">Unified theming with brand.yml</a> on the bslib website for instructions.</p>
</section>
<section id="brand.yml-in-r-markdown" class="level2">
<h2 data-anchor-id="brand.yml-in-r-markdown">brand.yml in R Markdown</h2>
<p>The R Markdown ecosystem widely <a href="https://rstudio.github.io/bslib/articles/any-project/index.html#r-markdown">use bslib for theming</a>. Anywhere that <code>theme</code> is passed to bslib, you can use brand.yml, either by placing a file named <code>_brand.yml</code> in the project or by passing a path to your brand.yml file to <code>brand</code>. Note that brand.yml works best with Bootstrap version 5.</p>
<p>For R Markdown, use <code>brand</code> under <code>output.html_document.theme</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>report.Rmd</strong></pre>
</div>
<div class="sourceCode" id="cb5" data-filename="report.Rmd" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">output</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb5-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">html_document</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb5-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb5-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb5-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brand</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> acme-brand.yml</span></span></code></pre></div>
</div>
<p>Similarly, in pkgdown, you can use <code>brand</code> under <code>template.bslib</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_pkgdown.yml</strong></pre>
</div>
<div class="sourceCode" id="cb6" data-filename="_pkgdown.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb6-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bslib</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb6-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span></span>
<span id="cb6-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brand</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> acme-brand.yml</span></span></code></pre></div>
</div>
<p>Note that <code>brand</code> isn’t strictly necessary: if you name your brand.yml file <code>_brand.yml</code>, bslib will automatically find it in your project<sup>1</sup>.</p>
</section>
<section id="looking-forward" class="level2">
<h2 data-anchor-id="looking-forward">Looking Forward</h2>
<p>We look forward to seeing how the community uses this feature and welcome your feedback! You can learn more about brand.yml and its features at the <a href="https://posit-dev.github.io/brand-yml">official brand.yml website</a>. Find more specific information about <a href="https://rstudio.github.io/bslib/articles/brand-yml/index.html">branded and custom theming with bslib</a> at the bslib website, or learn about <a href="https://posit.co/blog/unified-branding-across-posit-tools-with-brand-yml/">unified branding across Posit tools with brand.yml</a> on the Posit blog.</p>
<p>As a final tip, we know that writing YAML isn’t everyone’s cup of tea! If you want to enlist the help of a friendly large language model (LLM), we’ve written up <a href="https://posit-dev.github.io/brand-yml/articles/llm-brand-yml-prompt/">a guide to using LLMs to write brand.yml</a>.</p>
</section>
<div class="column-screen bg-light mt-4 page-columns page-full">
<section id="release-notes" class="level2 column-body">
<h2 data-anchor-id="release-notes">Thank you 💙</h2>
<p>This post doesn’t cover all of the changes and updates that happened in bslib in this release. To learn more about specific changes in each package, dive into the release notes linked below!</p>
<p><strong>A huge thank you</strong> to everyone who contributed pull requests, bug reports and feature requests.</p>
<section id="bslib-v0.9.0" class="level4">
<h4 data-anchor-id="bslib-v0.9.0">bslib <a href="https://rstudio.github.io/bslib/news/index.html#bslib-090">v0.9.0</a></h4>
<p><a href="https://github.com/al-obrien">@al-obrien</a>, <a href="https://github.com/AlbertRapp">@AlbertRapp</a>, <a href="https://github.com/CharlesBordet">@CharlesBordet</a>, <a href="https://github.com/cpsievert">@cpsievert</a>, <a href="https://github.com/cscheid">@cscheid</a>, <a href="https://github.com/daattali">@daattali</a>, <a href="https://github.com/danielloader">@danielloader</a>, <a href="https://github.com/DavZim">@DavZim</a>, <a href="https://github.com/DeepanshKhurana">@DeepanshKhurana</a>, <a href="https://github.com/dsen6644">@dsen6644</a>, <a href="https://github.com/dvg-p4">@dvg-p4</a>, <a href="https://github.com/eheinzen">@eheinzen</a>, <a href="https://github.com/furrrpanda">@furrrpanda</a>, <a href="https://github.com/gadenbuie">@gadenbuie</a>, <a href="https://github.com/grcatlin">@grcatlin</a>, <a href="https://github.com/ismirsehregal">@ismirsehregal</a>, <a href="https://github.com/jack-davison">@jack-davison</a>, <a href="https://github.com/LDSamson">@LDSamson</a>, <a href="https://github.com/lmullany">@lmullany</a>, <a href="https://github.com/luisDVA">@luisDVA</a>, <a href="https://github.com/lukebandy">@lukebandy</a>, <a href="https://github.com/matt-dray">@matt-dray</a>, <a href="https://github.com/meztez">@meztez</a>, <a href="https://github.com/natashanath">@natashanath</a>, <a href="https://github.com/olivroy">@olivroy</a>, <a href="https://github.com/RealKai42">@RealKai42</a>, <a href="https://github.com/royfrancis">@royfrancis</a>, <a href="https://github.com/see24">@see24</a>, <a href="https://github.com/SokolovAnatoliy">@SokolovAnatoliy</a>, <a href="https://github.com/Teebusch">@Teebusch</a>, <a href="https://github.com/udurraniAtPresage">@udurraniAtPresage</a>, and <a href="https://github.com/wulj5">@wulj5</a>.</p>
</section>
</section>
</div>




<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>If you don’t want bslib to use a project-level <code>_brand.yml</code> file, you can use <code>brand: false</code> to disable automatic discovery. Or you can use <code>brand: true</code> to ensure that a project <code>_brand.yml</code> is found. Finally, you could also use <code>brand</code> to provide an entire brand.yml definition in-line!↩︎</p></li>
</ol>
</section></div> ]]></description>
  <guid>https://shiny.posit.co/blog/posts/bslib-0.9.0/</guid>
  <pubDate>Fri, 31 Jan 2025 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/bslib-0.9.0/feature.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Branded theming for Shiny for Python apps</title>
  <dc:creator>Garrick Aden-Buie</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-python-1.2-brand-yml/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        


<div class="column-body-outset">
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.2-brand-yml/brand-yml-wide.svg" class="img-fluid" alt="brand.yml logo"></p>
</div>
<div class="lead">
<p>We are bursting to announce that Shiny for Python now supports <a href="https://posit-dev.github.io/brand-yml">brand.yml</a>, a simple, unified theming experience through a single YAML file.</p>
</div>
<section id="what-is-brand.yml" class="level2">
<h2 data-anchor-id="what-is-brand.yml">What is brand.yml?</h2>
<p><a href="https://posit-dev.github.io/brand-yml">brand.yml</a> simplifies brand management by consolidating your visual identity—colors, typography, and styling—into a single, easy-to-maintain YAML file.</p>
<p>For you: it’s a centralized source of truth for your brand or company’s visual identity. For the tools you use: it’s simple, structured data to coordinate theming of any output.</p>
<p>Here’s an example <code>_brand.yml</code> file that includes metadata about the company, its logos, color palette, theme, and the fonts and typographic settings used by the brand.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_brand.yml</strong></pre>
</div>
<div class="sourceCode" id="cb1" data-filename="_brand.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">meta</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand.yml</span></span>
<span id="cb1-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">link</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> https://posit-dev.github.io/brand-yml</span></span>
<span id="cb1-4"></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">logo</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span></span>
<span id="cb1-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">small</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand-yml-icon.svg</span></span>
<span id="cb1-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">medium</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand-yml-tall.svg</span></span>
<span id="cb1-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">large</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> brand-yml-wide.svg</span></span>
<span id="cb1-9"></span>
<span id="cb1-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">color</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">palette</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">orange</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#FF6F20"</span></span>
<span id="cb1-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pink</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#FF3D7F"</span></span>
<span id="cb1-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">primary</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> orange</span></span>
<span id="cb1-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">danger</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pink</span></span>
<span id="cb1-16"></span>
<span id="cb1-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typography</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fonts</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb1-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">family</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Open Sans</span></span>
<span id="cb1-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> google</span></span>
<span id="cb1-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">base</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Open Sans</span></span></code></pre></div>
</div>
<p>A core goal of brand.yml is to introduce a common syntax for theming across various data science outputs created with open-source tools. With brand.yml you won’t need to write multiple CSS files to maintain a consistent look across your apps, websites, and presentations.</p>
<p>And because all fields are optional, brand.yml also serves as a simple interface to theming across many outputs. For example, you could use brand.yml to set the base font to <a href="https://fonts.google.com/specimen/Roboto">Roboto</a> and use <span style="padding-inline:0.25em; background-color: #f96302; color: white;">orange</span> as the primary accent color.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>A minimal _brand.yml</strong></pre>
</div>
<div class="sourceCode" id="cb2" data-filename="A minimal _brand.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">color</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb2-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">primary</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#F96302"</span></span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typography</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb2-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fonts</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb2-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">family</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Roboto</span></span>
<span id="cb2-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> google</span></span>
<span id="cb2-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">base</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Roboto</span></span></code></pre></div>
</div>
<p>This same file can be used for your Shiny apps as well as your Quarto projects. We’re starting with Posit-sponsored projects like <a href="https://shiny.posit.co">Shiny</a>, <a href="https://quarto.org">Quarto</a>, and <a href="https://rmarkdown.rstudio.com">R Markdown</a>, but we’re hoping to see brand.yml adopted more widely in the open-source community. To encourage this, we’ve published the <a href="https://posit-dev.github.io/brand-yml/pkg/py">brand-yml Python package</a>, which you can use to integrate brand.yml into your project or other packages.</p>
<div class="callout callout-style-default callout-tip callout-titled" title="Writing brand.yml with the help of an LLM">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Writing brand.yml with the help of an LLM
</div>
</div>
<div class="callout-body-container callout-body">
<p>We know that writing YAML isn’t everyone’s cup of tea! If you want to enlist the help of a friendly large language model (LLM), we’ve written up <a href="https://posit-dev.github.io/brand-yml/articles/llm-brand-yml-prompt/">a guide to using LLMs to write brand.yml</a>.</p>
<p>The article includes a prompt you can copy and use to teach an LLM about the brand.yml syntax, as well as a few tips and follow-up prompts you can use to get the best results.</p>
</div>
</div>
</section>
<section id="getting-started" class="level2">
<h2 data-anchor-id="getting-started">Getting Started</h2>
<p>With Shiny for Python v1.2.0, using brand.yml to theme your Shiny app is straightforward. To get started, make sure you’ve installed the latest version of shiny with the <code>theme</code> extra:</p>
<div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shiny[theme]"</span></span></code></pre></div>
<p>Then, create a <code>_brand.yml</code> file in your project directory (either alongside your <code>app.py</code> or in a parent folder). Finally, use the <a href="https://shiny.posit.co/py/api/core/ui.Theme.html#shiny.ui.Theme.from_brand">new <code>ui.Theme.from_brand()</code> function</a> to set your page <code>theme</code>.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="shiny-app-mode">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true">Express</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false">Core</a></li></ul>
<div class="tab-content" data-group="shiny-app-mode">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>app.py</strong></pre>
</div>
<div class="sourceCode" id="cb4" data-filename="app.py" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span>, render, ui</span>
<span id="cb4-2"></span>
<span id="cb4-3">ui.page_opts(theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.Theme.from_brand(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>))</span></code></pre></div>
</div>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>app.py</strong></pre>
</div>
<div class="sourceCode" id="cb5" data-filename="app.py" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> ui</span>
<span id="cb5-2"></span>
<span id="cb5-3">app_ui <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ui.page_fluid(</span>
<span id="cb5-4">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># App UI code...</span></span>
<span id="cb5-5">    theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ui.Theme.from_brand(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__file__</span>)</span>
<span id="cb5-6">)</span></code></pre></div>
</div>
</div>
</div>
</div>
<p>We’ve added a <a href="https://shinylive.io/py/examples/#branded-theming">complete branded theming example</a> to <a href="https://shinylive.io/py/examples/#branded-theming">shinylive.io</a> that you can run directly in your browser. Use it to explore brand.yml features or to preview your own company’s <code>_brand.yml</code> look in Shiny.</p>
</section>
<section id="looking-forward" class="level2">
<h2 data-anchor-id="looking-forward">Looking Forward</h2>
<p>We’re excited to see how the community uses this feature and look forward to your feedback! You can learn more about brand.yml and its features at the <a href="https://posit-dev.github.io/brand-yml">official brand.yml website</a>. Or read more about <a href="https://shiny.posit.co/py/api/core/ui.Theme.html#shiny.ui.Theme.from_brand">branded and custom theming in Shiny for Python</a>.</p>
<p>For Python developers, we encourage you to check out the <a href="https://posit-dev.github.io/brand-yml/pkg/py/">brand_yml Python package</a>, which provides an easy-to-use interface for parsing and making use of brand.yml files.</p>
<p>For R and Shiny for R developers, hang tight! Shiny for R support is next on our roadmap, followed by additional tooling to support reading and using brand.yml files.</p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-python-1.2-brand-yml/</guid>
  <pubDate>Mon, 25 Nov 2024 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-python-1.2-brand-yml/brand-yml-feature.svg" medium="image" type="image/svg+xml"/>
</item>
<item>
  <title>posit::conf(2024) Shiny talks</title>
  <dc:creator>Andrew Holz</dc:creator>
  <link>https://shiny.posit.co/blog/posts/conf-2024-shinytalks/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        


<p>Videos of posit::conf(2024) talks are now posted <a href="https://www.youtube.com/watch?v=s_Vh9HIeLVg&amp;list=PL9HYL-VRX0oSFkdF4fJeY63eGDvgofcbn">on YouTube</a>. We have also made <a href="https://youtube.com/playlist?list=PLitrm9UndxcsoP0pqVvteJWtAQ9TKT-mf">playlist of talks</a> that are about building data driven applications with Shiny.</p>
<div class="video-container my-5">
<iframe width="560" height="315" src="https://www.youtube.com/embed/videoseries?si=b5ZCT7LMUOXamjoR&amp;list=PLitrm9UndxcsoP0pqVvteJWtAQ9TKT-mf" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="">
</iframe>
</div>
<p>Talks included in this playlist are as follows:</p>
<ul>
<li><a href="https://youtu.be/s_cBi155ZIQ?si=RCXLXY0XHeVLFc4k"><strong>Charlie Gao and Will Landau</strong> - {mirai} and {crew}: next-generation async to supercharge {promises}, Plumber, Shiny, and {targets}</a></li>
<li><a href="https://youtu.be/iC78WbnwnIs?si=2GDss6vGoWL-BSRm"><strong>Eric Nantz</strong> - A New Era for Shiny-based Clinical Submissions using WebAssembly</a></li>
<li><a href="https://youtu.be/G2GNZZ8GQPY?si=m7WIT4-JJaG39Gtw"><strong>Marcin Dubel</strong> - Shiny in Action: Transforming Film Production with TARS</a></li>
<li><a href="https://youtu.be/SAbV6d7Pn0U?si=AVd2M9a7yeZ67dIF"><strong>Kiegan Rice</strong> - Wait, that’s Shiny? Building feature-full, user-friendly interactive data explorers with Shiny and friends</a></li>
<li><a href="https://youtu.be/FPc5PJRWHsk?si=W484jzz4EyouBCI8"><strong>Greg Swinehart</strong> - We CAN Have Nice Shiny Apps</a></li>
<li><a href="https://youtu.be/zgcyGs9q06g?si=4iNmTxsoYy5FmWK-"><strong>Andrew Bates</strong> - Making an App a System</a></li>
<li><a href="https://youtu.be/59ztetVfmjg?si=uFD7nn6qEl38WtgW"><strong>Lovekumar Patel</strong> - Empowering Decisions: Advanced Portfolio Analysis and Management through Shiny</a></li>
<li><a href="https://youtu.be/TYHiwWlpGOM?si=72zm3YKjncZWE9xg"><strong>Winston Chang</strong> - Building ML and AI apps with Shiny for Python</a></li>
<li><a href="https://youtu.be/pL44iKfcECU?si=_iD8mvy76Vf6Kosy"><strong>Carson Sievert</strong> - Supercharge Your Shiny (for Python) App: Unleashing Interactive Jupyter Widgets</a></li>
<li><a href="https://youtu.be/AP8BWGhCRZc?si=otdkZ1N1H0VD707k"><strong>Joe Cheng</strong> - Shiny x AI</a></li>
<li><a href="https://youtu.be/AXH52WNOErc?si=dCrLS3pJFd2wVTW-"><strong>Barret Schloerke</strong> - Editable data frames in Py-Shiny: Updating original data in real-time</a></li>
<li><a href="https://youtu.be/kYnqxQvIFNQ?si=Ai6bAjxhSxWt2c5h"><strong>Joseph Richey</strong> - Leveraging Data in a Volunteer Fire Department</a></li>
<li><a href="https://youtu.be/_AFa4DIGDJk?si=wdFmHswXNIVHYA3f"><strong>Mark Wang</strong> - Using GitHub Copilot in R Shiny Development</a></li>
</ul>



 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/conf-2024-shinytalks/</guid>
  <pubDate>Mon, 11 Nov 2024 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/conf-2024-shinytalks/shinytalks.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Shiny for Python 1.2.0</title>
  <dc:creator>Shiny Team</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-python-1.2/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-python-1.2/shinyforpython-120.jpg" alt="Shiny for Python 1.2.0" style="width: 100%; height: auto;">
    </div>
    


<p>Shiny for Python <code>v1.2.0</code> is here! To celebrate, let’s highlight a big new feature of this release: integration with <a href="https://narwhals-dev.github.io/narwhals/"><code>narwhals</code></a> for <code>@render.data_frame</code>! 🐳🦄</p>
<p>In a follow up post, we’ll also highlight another new exciting feature: integration with brand new <a href="https://github.com/posit-dev/brand-yml"><code>brand-yml</code></a> package for theming.</p>
<p>For a full list of all the changes in this release, check out the <a href="https://github.com/posit-dev/py-shiny/blob/main/CHANGELOG.md#120---2024-10-29">CHANGELOG</a>.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
What is Shiny for Python?
</div>
</div>
<div class="callout-body-container callout-body">
<p>Shiny for Python is a framework that makes it easy to build interactive web applications using Python (<a href="https://shiny.posit.co/py/docs/">Quick Start</a>). You can create web applications directly from your Python code without needing to know any Javascript. Shiny is built on a <a href="https://shiny.posit.co/py/docs/reactive-foundations.html">reactive programming model</a>, meaning that Shiny automatically figures out the relationships between different components in your application. When a user interacts with your application, Shiny will only update the necessary parts. This ensures that your applications stay fast and responsive even as they grow in size and complexity.</p>
</div>
</div>
<div class="pt-2 pb-1">
<hr>
</div>
<section id="narwhals-integration" class="level2">
<h2 data-anchor-id="narwhals-integration"><code>narwhals</code> integration</h2>
<p>In the Shiny <code>v1.0.0</code> release, <code>@render.data_frame</code> added support for <a href="https://docs.pola.rs/">Polars</a> data frames (in addition to the existing support for <a href="https://pandas.pydata.org/">pandas</a> data frames). This was performed through custom inspection and handling for the two supported data frame types. While this worked, it was not ideal for users who wanted use a <em>new</em> data frame type. This would require Shiny to implement custom code for each new data frame type of which we are not experts. This approach does not scale well!</p>
<p>In Shiny <code>v1.2.0</code>, <code>@render.data_frame</code> integrated with <a href="https://narwhals-dev.github.io/narwhals/">narwhals</a> better data frame support! 🥳🥳</p>
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.2/narwhals_w_shiny.jpeg" class="img-fluid"></p>
<p><code>narwhals</code> describes itself as an <em>“Extremely lightweight and extensible compatibility layer between dataframe libraries!”</em> that can <em>“Seamlessly support all, without depending on any!”</em>. <code>narwhals</code> (as of this blog post) has full API support for eager data frames such as <a href="https://docs.rapids.ai/api/cudf/stable/">cuDF</a>, <a href="https://modin.readthedocs.io/en/stable/">Modin</a>, <a href="https://pandas.pydata.org/">pandas</a>, <a href="https://docs.pola.rs/">Polars</a>, and <a href="https://arrow.apache.org/docs/python/">PyArrow</a>. This means that you can immediately use any of these data frame libraries (even <code>narwhals</code> itself) within Shiny’s <code>@render.data_frame</code> without any updates to Shiny or forcing you to export your data to pandas or Polars. Additionally, as <code>narwhals</code> (who is rapidly improving by the day) adds support for new data frame libraries, Shiny will automatically support them as well! As <code>narwhals</code> improves, so does Shiny! 🚀</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Additional Narwhals data frame types
</div>
</div>
<div class="callout-body-container callout-body">
<p>Narwhals also supports other styles of data frames such as <a href="https://docs.dask.org/en/stable/">Dask</a> (Lazy-only) and <a href="https://docs.ibis-project.org/">Ibis</a> and <a href="https://vaex.io/docs/index.html">Vaex</a> through the DataFrame Interchange Protocol. However, these data frame types are not directly supported within Shiny as they are not <em>eager</em> data frames.</p>
<p>Please convert your lazy or interchange data to an eager data frame before returning it within <code>@render.data_frame</code>.</p>
</div>
</div>
<p>Let’s look at an example that uses a PyArrow Table (which has never been directly implemented by Shiny) displaying <code>great_tables</code>’s S&amp;P 500 data:</p>
<div class="pt-3">
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>app.py</strong></pre>
</div>
<div class="sourceCode" id="cb1" data-filename="app.py" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> great_tables <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> gt</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> pyarrow <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> pa</span>
<span id="cb1-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> shiny.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> render, ui</span>
<span id="cb1-4"></span>
<span id="cb1-5">pa_data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pa.Table.from_pandas(gt.data.sp500)</span>
<span id="cb1-6"></span>
<span id="cb1-7">ui.h3(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"S&amp;P 500 (PyArrow)"</span>)</span>
<span id="cb1-8"></span>
<span id="cb1-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@render.data_frame</span></span>
<span id="cb1-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">def</span> pa_df():</span>
<span id="cb1-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-style: inherit;">return</span> pa_data</span></code></pre></div>
</div>
</div>
<div class="border rounded p-2 pb-0 mb-5">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://shiny.posit.co/blog/posts/shiny-python-1.2/pa-sp500.gif" class="w-100 img-fluid figure-img"></p>
<figcaption>PyArrow Table; 16,607 rows</figcaption>
</figure>
</div>
</div>
<p>For a quick demo, Barret Schloerke will talk about the example above and more!</p>
<div class="quarto-video ratio ratio-16x9"><iframe data-external="1" src="https://www.youtube.com/embed/W-_0rkcuB_8" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
<div class="pt-2 pb-1">
<hr>
</div>
</section>
<section id="new-methods-for-render.data_frame" class="level2">
<h2 data-anchor-id="new-methods-for-render.data_frame">New methods for <code>@render.data_frame</code></h2>
<p>In addition to the integration of <code>narwhals</code>, we have added new instance methods to <code>@render.data_frame</code> objects to make it easier to work with data frames.</p>
<ul>
<li><code>.update_data(data)</code>
<ul>
<li>Updates the <code>.data()</code> data frame with a new value.</li>
<li>The user’s sorting and filtering will not be reset. However, all <code>.cell_patches()</code> patches (user edits) will be removed.</li>
</ul></li>
<li><code>.update_cell_value(value, *, row, col)</code>
<ul>
<li>Updates a single value of a cell in the data frame.</li>
<li><code>.cell_patches()</code> patches which are used to create <code>.data_patched()</code> from <code>.data()</code>.</li>
</ul></li>
<li><code>.data_patched()</code>
<ul>
<li>Reactive calculation of the <code>.data()</code> data frame value with all <code>.cell_patches()</code> patches applied.</li>
<li>Unlike <code>.data_view()</code>, <code>.data_patched()</code> will not be affected by user sorting, filtering, or row selections.</li>
<li>The new data set can be of completely different types and shape. For typing purposes, it is strongly recommended to use the same type as the original data set.</li>
</ul></li>
</ul>
<section id="learn-more" class="level3">
<h3 data-anchor-id="learn-more">Learn more</h3>
<p>For a comprehensive overview of new and old data frame features, see the updated articles on <a href="https://shiny.posit.co/py/components/outputs/data-grid/">DataGrid</a> and <a href="https://shiny.posit.co/py/components/outputs/data-table/">DataTable</a>.</p>
<div class="pt-2 pb-1">
<hr>
</div>
<p>We’re thrilled to bring you these new features and improvements in Shiny for Python 1.2.0. As always, if you have any questions or feedback, please <a href="https://discord.gg/yMGCamUMnS">join us on Discord</a> or <a href="https://github.com/posit-dev/py-shiny/issues/new">open an issue on GitHub</a>. Happy Shiny-ing!</p>


</section>
</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-python-1.2/</guid>
  <pubDate>Thu, 31 Oct 2024 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-python-1.2/shinyforpython-120.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Testing Shiny Apps in Python</title>
  <dc:creator>Shiny Team</dc:creator>
  <link>https://shiny.posit.co/blog/posts/shiny-python-testing/</link>
  <description><![CDATA[ 
<!-- Google Tag Manager (noscript) -->
 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KHBDBW7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<style type="text/css">
  #quarto-content>* {
  padding-top: 0px;
  }
</style>


        <div class="column-body-outset blog-image-container pb-5">
      <img src="https://shiny.posit.co/blog/posts/shiny-python-testing/testing-og.jpg" style="width: 100%; height: auto;">
    </div>
    


<p>Shiny for Python is a framework that helps you create interactive web applications using Python. It features reactive programming, allowing developers to build dynamic user interfaces without having to write JavaScript or HTML.</p>
<p>Writing <a href="https://shiny.posit.co/py/docs/end-to-end-testing.html">end-to-end tests for your Shiny apps</a> is essential to ensure the application works as intended from the user’s perspective. These tests identify issues in the user interface and interactions, ensuring a seamless experience.</p>
<div class="callout callout-style-default callout-note callout-titled" title="Install Shiny for Python">
<div class="callout-header d-flex align-content-center" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Install Shiny for Python
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>To install the Shiny for Python package, users can use the following command in their terminal or command prompt:</p>
<div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pip</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"shiny&gt;=1.0"</span></span></code></pre></div>
</div>
</div>
</div>
<section id="automatically-create-tests-for-shiny-apps-using-shiny-add-test-command" class="level3">
<h3 data-anchor-id="automatically-create-tests-for-shiny-apps-using-shiny-add-test-command">Automatically create tests for Shiny apps using <code>shiny add test</code> command</h3>
<p><code>shiny</code> for Python provides a simple way to create a test file for your Shiny app. To add a test, type <code>shiny add test</code> in your terminal/console and give the <strong>path to the Shiny app file</strong> along with the <strong>path of the test file</strong>. Shiny will create a test file for your Shiny app that the user can then run using <code>pytest</code>.</p>
<div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">shiny</span> add test</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">?</span> Enter the path to the app file: basic-app/app.py</span>
<span id="cb2-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">?</span> Enter the path to the test file: ./basic-app/tests/test_app.py</span>
<span id="cb2-5"></span>
<span id="cb2-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Next</span> steps:</span>
<span id="cb2-7"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">-</span> Run <span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">`</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">pytest</span><span class="kw" style="color: #003B4F;
background-color: null;
font-style: inherit;">`</span> in your terminal to run all the tests</span></code></pre></div>
<p>Once the test file is created, leverage the in-built controllers for each of the input and output components to expedite writing tests.</p>
<div class="quarto-video ratio ratio-16x9"><iframe data-external="1" src="https://www.youtube.com/embed/oJ_B_NuFAwU" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
</section>
<section id="using-controllers-to-write-robust-shiny-app-tests" class="level3">
<h3 data-anchor-id="using-controllers-to-write-robust-shiny-app-tests">Using controllers to write robust Shiny app tests</h3>
<p><code>shiny</code> exposes UI testing controllers used to interact with existing Shiny components during tests.</p>
<blockquote class="blockquote">
<p>Why did we expose controllers?</p>
</blockquote>
<p>Controllers were introduced to give users a clear and consistent way to interact with and test UI components in Shiny applications. They simplify the process by hiding the complexity of working with specific UI elements, making it easier for developers to create reliable tests and automate interactions with their Shiny apps.</p>
<blockquote class="blockquote">
<p>What are the different controllers available?</p>
</blockquote>
<p>There are controllers for various UI components commonly used in Shiny applications, such as:</p>
<ul>
<li>Input fields</li>
<li>Checkboxes and radio buttons</li>
<li>Date pickers</li>
<li>Sliders</li>
<li>Output Plots and more.</li>
</ul>
<p>You can find the full list of available controllers in our <a href="https://shiny.posit.co/py/api/testing/">function reference</a> page.</p>
<p>Here’s a preview of that feature.</p>
<div class="quarto-video ratio ratio-16x9"><iframe data-external="1" src="https://www.youtube.com/embed/ecVPjqsVSkU" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
<blockquote class="blockquote">
<p>Why are there so many controllers?</p>
</blockquote>
<p>There are many controllers designed to work with the wide variety of UI components in Shiny applications. Each UI element has its own unique properties, behaviors, and ways of interacting. By offering specific controllers for each type of component, developers have the right tools to interact with and test every part of their Shiny app’s interface.</p>
<blockquote class="blockquote">
<p>What are the benefits of using controllers?</p>
</blockquote>
<ol type="a">
<li><p><em><strong>Abstraction</strong></em>: Controllers simplify interactions with UI elements by providing a cleaner, more intuitive API for developers.</p></li>
<li><p><em><strong>Consistency</strong></em>: They ensure a uniform interface for different UI components, making tests easier to write and maintain.</p></li>
<li><p><em><strong>Reliability</strong></em>: Controllers include robust waiting and expectation mechanisms, minimizing test flakiness from timing issues or asynchronous updates.</p></li>
<li><p><em><strong>Readability</strong></em>: Tests using controllers are more readable and self-documenting, as the methods clearly convey the purpose of each interaction or assertion.</p></li>
</ol>
<blockquote class="blockquote">
<p>How about expanding or collapsing the accordion panels in your shiny app for the tests?</p>
</blockquote>
<p>I am glad you asked. There is an <code>AccordionPanel</code> controller for that case.</p>
<blockquote class="blockquote">
<p>Do you want to move the slider in your shiny app as part of the test?</p>
</blockquote>
<p>There is a controller of <code>InputSlider</code> that makes it a breeze to help with that.</p>


</section>

 ]]></description>
  <guid>https://shiny.posit.co/blog/posts/shiny-python-testing/</guid>
  <pubDate>Tue, 29 Oct 2024 00:00:00 GMT</pubDate>
  <media:content url="https://shiny.posit.co/blog/posts/shiny-python-testing/testing-og.jpg" medium="image" type="image/jpeg"/>
</item>
</channel>
</rss>
