<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mariano Álvarez 🇨🇷</title>
    <description>The latest articles on DEV Community by Mariano Álvarez 🇨🇷 (@marianocodes).</description>
    <link>https://dev.to/marianocodes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F220813%2F08bdd770-d41f-4f2c-9773-fee816e76234.jpg</url>
      <title>DEV Community: Mariano Álvarez 🇨🇷</title>
      <link>https://dev.to/marianocodes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marianocodes"/>
    <language>en</language>
    <item>
      <title>Baseline Web Features You Can Safely Use Today to Boost Performance</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Thu, 01 Jan 2026 01:29:18 +0000</pubDate>
      <link>https://dev.to/marianocodes/baseline-web-features-you-can-safely-use-today-to-boost-performance-4bnb</link>
      <guid>https://dev.to/marianocodes/baseline-web-features-you-can-safely-use-today-to-boost-performance-4bnb</guid>
      <description>&lt;p&gt;Performance optimization can feel like a gamble. You find an amazing CSS property or API that could shave hundreds of milliseconds off your load time, but then you check browser support and… it's a sea of red on Can I Use. So you give up and stick with the same old techniques.&lt;/p&gt;

&lt;p&gt;Here's the thing: there's a growing list of powerful performance features that are now &lt;strong&gt;Baseline Widely Available&lt;/strong&gt;. That means they've been supported across all major browsers for at least 30 months. You can use them today without worrying about compatibility.&lt;/p&gt;

&lt;p&gt;Let me show you four that should be in your toolkit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does "Baseline" Mean?
&lt;/h2&gt;

&lt;p&gt;Before we dive in, quick context: &lt;a href="https://web.dev/baseline" rel="noopener noreferrer"&gt;Baseline&lt;/a&gt; is a compatibility initiative from the Web Platform that identifies when features are safe to use across popular browsers. A feature becomes &lt;strong&gt;Baseline Widely Available&lt;/strong&gt; after it's been supported in Chrome, Edge, Firefox, and Safari for 30+ months.&lt;/p&gt;

&lt;p&gt;Think of it as a green light for production use. No polyfills needed, no edge case workarounds—just reliable browser support.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. content-visibility: Skip Rendering Work You Don't Need
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;content-visibility&lt;/code&gt; CSS property lets browsers skip rendering work—layout, painting, all of it—for offscreen content. This is particularly powerful for long pages with lots of DOM elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chrome/Edge: 85+&lt;/li&gt;
&lt;li&gt;Firefox: 125+&lt;/li&gt;
&lt;li&gt;Safari: 18+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Baseline Newly available (September 2025)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.article-section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;content-visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;contain-intrinsic-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;auto&lt;/code&gt; value tells the browser to skip rendering when the element is offscreen. The &lt;code&gt;contain-intrinsic-size&lt;/code&gt; acts as a placeholder to prevent layout shifts—it's essentially telling the browser "assume this element is about 1000px tall until you actually render it."&lt;/p&gt;

&lt;p&gt;If you're building something like an infinite scroller, use the &lt;code&gt;auto&lt;/code&gt; keyword with &lt;code&gt;contain-intrinsic-size&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.feed-item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;content-visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;contain-intrinsic-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="m"&gt;500px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;auto&lt;/code&gt; keyword makes the browser remember the last rendered size, which is perfect for content that might be scrolled back into view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real Performance Gains
&lt;/h3&gt;

&lt;p&gt;In a travel blog example from web.dev, applying &lt;code&gt;content-visibility: auto&lt;/code&gt; reduced initial rendering time from &lt;strong&gt;232ms to 30ms&lt;/strong&gt;—a 7x improvement. That's the kind of boost that directly impacts Interaction to Next Paint (INP).&lt;/p&gt;

&lt;h3&gt;
  
  
  Important Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Content remains searchable and accessible (unlike &lt;code&gt;visibility: hidden&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Avoid calling DOM APIs that force layout on skipped subtrees&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;aria-hidden="true"&lt;/code&gt; on landmark elements if you're getting accessibility tree clutter&lt;/li&gt;
&lt;li&gt;Works best on chunked content sections (articles, feed items, product cards)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. AVIF: Next-Gen Image Compression
&lt;/h2&gt;

&lt;p&gt;AVIF is an image format built on the AV1 video codec. It delivers &lt;strong&gt;greater than 50% file size savings compared to JPEG&lt;/strong&gt; while maintaining the same visual quality. If you're trying to optimize Largest Contentful Paint (LCP), this one's a game-changer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chrome: 85+&lt;/li&gt;
&lt;li&gt;Edge: 93+&lt;/li&gt;
&lt;li&gt;Firefox: 93+&lt;/li&gt;
&lt;li&gt;Safari: 16+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Baseline Widely Available&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Smaller images = faster downloads = better LCP scores. AVIF also supports High Dynamic Range, Wide Color Gamut, and progressive decoding. Think of it as the image format designed for modern web performance needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;The simplest approach uses the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element with fallbacks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero-image.avif"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero-image.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"hero-image.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Hero image"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers try formats from top to bottom. AVIF-capable browsers get the smallest file, others fall back to WebP or JPEG.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encoding Considerations
&lt;/h3&gt;

&lt;p&gt;If you're encoding AVIF images yourself using &lt;code&gt;avifenc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;avifenc &lt;span class="nt"&gt;--cq-level&lt;/span&gt; 23 &lt;span class="nt"&gt;--speed&lt;/span&gt; 6 &lt;span class="nt"&gt;--jobs&lt;/span&gt; 8 input.jpg output.avif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cq-level&lt;/code&gt;&lt;/strong&gt;: Quality setting (0-63, lower = higher quality). Start around 23-28 for photos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--speed&lt;/code&gt;&lt;/strong&gt;: Encoding speed (0-10, default 6). Lower = slower encoding but better compression.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--jobs&lt;/code&gt;&lt;/strong&gt;: Multi-threading. Using 8 threads gives roughly 5x speedup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Practical Advice
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use lossy encoding for photographs, lossless for line art&lt;/li&gt;
&lt;li&gt;Experiment with quality settings—sometimes &lt;code&gt;cq-level 28&lt;/code&gt; looks identical to &lt;code&gt;cq-level 23&lt;/code&gt; but saves another 20%&lt;/li&gt;
&lt;li&gt;Consider image CDNs (Cloudinary, Imgix) if you don't want to manage encoding yourself&lt;/li&gt;
&lt;li&gt;Always provide fallbacks for older browsers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Module Preload: Fix Dependency Waterfalls
&lt;/h2&gt;

&lt;p&gt;If you're shipping ES modules in production, you've probably noticed dependency waterfalls. The browser fetches your entry module, parses it, discovers imports, fetches those, discovers more imports… it's roundtrip city.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;link rel="modulepreload"&amp;gt;&lt;/code&gt; solves this by letting you declaratively tell the browser to fetch and compile modules ahead of time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chrome: 66+&lt;/li&gt;
&lt;li&gt;Edge: 79+&lt;/li&gt;
&lt;li&gt;Firefox: 115+&lt;/li&gt;
&lt;li&gt;Safari: 17+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Baseline Widely Available&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Standard Preload Doesn't Work
&lt;/h3&gt;

&lt;p&gt;You might think &lt;code&gt;&amp;lt;link rel="preload" as="script"&amp;gt;&lt;/code&gt; would handle this. It doesn't, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modules use an &lt;code&gt;omit&lt;/code&gt; credentials mode that standard preload doesn't support&lt;/li&gt;
&lt;li&gt;The browser doesn't know to parse/compile the file as a module&lt;/li&gt;
&lt;li&gt;Chrome's V8 engine handles module compilation differently than regular scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to Use It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Preload your critical modules --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/app.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/router.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/components.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/app.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser fetches all three modules in parallel, parses and compiles them immediately, then executes when needed. No more dependency waterfalls.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use It
&lt;/h3&gt;

&lt;p&gt;Module preload is most effective when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have deep module dependency chains (3+ levels)&lt;/li&gt;
&lt;li&gt;You're seeing performance issues from roundtrip delays&lt;/li&gt;
&lt;li&gt;Your application has a clear critical path of modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For shallow dependency trees (1-2 levels), the overhead might not be worth it. Profile first, optimize second.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Word of Caution
&lt;/h3&gt;

&lt;p&gt;Listing every module defeats the purpose. Focus on the critical path—the modules absolutely needed for initial render. Think of it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Good: Critical path only --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/app.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/critical-component.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Bad: Everything and the kitchen sink --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/utils.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/helpers.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/random-thing-used-once.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Server-Timing: Surface Backend Performance Metrics
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Server-Timing&lt;/code&gt; HTTP header lets you expose backend performance metrics directly in the browser's DevTools and JavaScript. It's like having a window into your server's performance for every request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chrome: 65+&lt;/li&gt;
&lt;li&gt;Edge: 79+&lt;/li&gt;
&lt;li&gt;Firefox: 61+&lt;/li&gt;
&lt;li&gt;Safari: 16.4+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Baseline Widely Available (March 2023)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What You Can Track
&lt;/h3&gt;

&lt;p&gt;Database queries, cache hits/misses, API calls, CPU time, file system access—basically any server-side operation you want visibility into.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Implementation
&lt;/h3&gt;

&lt;p&gt;On the server, add timing headers to your responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Server-Timing: db;dur=53.2;desc="Database query"
Server-Timing: cache;dur=2.1;desc="Cache read"
Server-Timing: api;dur=127.8;desc="External API call"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or as a single header with multiple metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Server-Timing: db;dur=53.2;desc="Database", cache;dur=2.1;desc="Cache", api;dur=127.8;desc="API call"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Node.js Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Track database time&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM users WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbDuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;dbStart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Track cache time&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheDuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;cacheStart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Set Server-Timing header&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server-Timing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`
    db;dur=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dbDuration&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;desc="User query",
    cache;dur=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cacheDuration&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;desc="Cache check",
    total;dur=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;desc="Total"
  `&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Accessing in JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get timing data for all resources&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverTiming&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Example output:&lt;/span&gt;
&lt;span class="c1"&gt;// db: 53.2ms - User query&lt;/span&gt;
&lt;span class="c1"&gt;// cache: 2.1ms - Cache check&lt;/span&gt;
&lt;span class="c1"&gt;// total: 178.4ms - Total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DevTools Integration
&lt;/h3&gt;

&lt;p&gt;The best part? These metrics show up automatically in Chrome DevTools under the &lt;strong&gt;Timing&lt;/strong&gt; tab for each request. No special configuration needed—just set the header and open DevTools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Considerations
&lt;/h3&gt;

&lt;p&gt;Be thoughtful about what you expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid revealing sensitive infrastructure details&lt;/li&gt;
&lt;li&gt;Consider exposing detailed metrics only to authenticated users&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Timing-Allow-Origin&lt;/code&gt; header to control cross-origin access&lt;/li&gt;
&lt;li&gt;Don't expose internal service names or database schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For public APIs, stick to high-level metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Server-Timing: total;dur=245;desc="Processing time"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For internal tools or authenticated users, go wild with detail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Server-Timing: db-users;dur=23;desc="Users table", db-posts;dur=45;desc="Posts table", redis;dur=3;desc="Cache", s3;dur=89;desc="Asset storage"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Here's what a performance-optimized page might look like using all four features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;High Performance Page&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Module preload for critical JavaScript --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/app.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"modulepreload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/js/router.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;/* content-visibility for long sections */&lt;/span&gt;
    &lt;span class="nc"&gt;.article-section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;content-visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;contain-intrinsic-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="m"&gt;800px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- AVIF images with fallbacks --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero.avif"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"hero.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Hero"&lt;/span&gt; &lt;span class="na"&gt;fetchpriority=&lt;/span&gt;&lt;span class="s"&gt;"high"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"article-section"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- Content here --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"article-section"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- More content --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/js/app.mjs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Log server timing metrics&lt;/span&gt;
    &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntriesByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverTiming&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; took &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express.js example&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Your request handling with timing tracking...&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server-Timing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;These four features are production-ready today. They're not experimental, they're not behind flags, and you don't need polyfills. They're just solid, well-supported tools that can genuinely improve your site's performance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;content-visibility&lt;/strong&gt;: Reduce rendering work for offscreen content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AVIF&lt;/strong&gt;: Cut image file sizes in half&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module preload&lt;/strong&gt;: Eliminate dependency waterfalls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-Timing&lt;/strong&gt;: Surface backend performance metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combination has noticeably improved Core Web Vitals scores—particularly LCP and INP. &lt;/p&gt;

&lt;p&gt;Start with one. Add AVIF support to your hero images. Try &lt;code&gt;content-visibility&lt;/code&gt; on a long article page. Add module preload to your critical JavaScript. The beauty of Baseline features is you can adopt them incrementally without worrying about breaking browsers.&lt;/p&gt;




&lt;p&gt;Want to dive deeper? Check out the official documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/content-visibility" rel="noopener noreferrer"&gt;content-visibility on web.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/compress-images-avif" rel="noopener noreferrer"&gt;AVIF compression guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/modulepreload" rel="noopener noreferrer"&gt;Module preload explained&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/docs/Web/HTTP/Headers/Server-Timing" rel="noopener noreferrer"&gt;Server-Timing on MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/baseline" rel="noopener noreferrer"&gt;What is Baseline?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you found this useful, a like or share helps others discover it. And if you've used any of these features in production, I'd love to hear about your experience in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>javascript</category>
      <category>web</category>
    </item>
    <item>
      <title>Gemini, Local and Free with Chrome and Angular</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Tue, 30 Dec 2025 04:00:16 +0000</pubDate>
      <link>https://dev.to/marianocodes/gemini-locally-and-free-with-chrome-and-angular-474l</link>
      <guid>https://dev.to/marianocodes/gemini-locally-and-free-with-chrome-and-angular-474l</guid>
      <description>&lt;p&gt;AI is impressive, but it is also expensive. In today’s landscape, developers need smarter ways to add AI-powered features without burning through budgets. Fortunately, the Chrome team has been working on a set of on-device AI APIs that let you run models locally, trained for specific tasks, at zero inference cost.&lt;/p&gt;

&lt;p&gt;Some of the available APIs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language API&lt;/strong&gt;: Detects the language of a given text
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation API&lt;/strong&gt;: Translates text from one language to another
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt API&lt;/strong&gt;: Accepts free-form prompts for structured outputs
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summarization API&lt;/strong&gt;: Condenses long text into concise summaries
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, I’ll show how you can take advantage of these APIs to add genuinely useful features without paying for tokens.&lt;/p&gt;

&lt;p&gt;One thing I find particularly interesting is how Google Search now often gives you an AI-generated summary or even a direct answer. Sometimes you just want the takeaway, not the entire article. That idea is exactly what we’re going to replicate here.&lt;/p&gt;

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

&lt;p&gt;The goal is simple: build a small piece of logic that generates a TL;DR (too long to read) section in bullet points for a blog post, so readers can instantly understand what it’s about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Make sure you are running the latest version of Chrome. Older versions may not include the most recent on-device models.&lt;/p&gt;

&lt;p&gt;Next, enable the following flags and restart Chrome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#optimization-guide-on-device-model&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#prompt-api-for-gemini-nano-multimodal-input&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once that’s done, install the models by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;downloadprogress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Downloaded &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summarizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Summarizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;downloadprogress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Downloaded &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the download reaches 100%, you are ready to use the APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;We’ll use Angular for the implementation. If you don’t already have it set up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the Angular CLI: &lt;code&gt;npm install -g @angular/cli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to your desired folder and run: &lt;code&gt;ng new &amp;lt;name-project&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To speed things up, we’ll use Google’s new IDE, Antigravity, which relies on agents to generate code. The following prompt describes the UI we want to build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
You are an expert Frontend Developer. Your task is to build a premium, visually stunning blog post page for an Angular application. The project is already initialized.

### Goal
Create a standalone component named `BlogPost` that serves as a static blog post page. The design should be modern, "dark mode" by default, and evoke a high-tech, futuristic feel suitable for the topic of "Agentic AI".

### Structure &amp;amp; Content Constraints
The page must contain the following specific elements, stacked vertically:

1.  **Header Section**:
    *   **Tags**: A row of small pill-shaped tags: "Artificial Intelligence", "Future", "Tech".
    *   **Title**: Large, impactful typography: "The Rise of Agentic AI: A New Era of Coding".
    *   **Subtitle**: A lighter sub-heading: "How autonomous agents are transforming the software development landscape".

2.  **TL;DR Section**:
    *   Placed prominently below the header but before the main content.
    *   This section must clearly stand out from the rest of the text (e.g., using a border, different background tint, or accent color).
    *   **Heading**: "TL;DR".
    *   **Content**: A bulleted list summarizing the article (e.g., AI moving from autocomplete to autonomy, changing developer roles to architects).

3.  **Main Content**:
    *   Several paragraphs of text discussing "Agentic AI".
    *   Explain how it differs from traditional coding assistants.
    *   Discuss the shift from "writing code" to "guiding agents".
    *   Use highly readable typography with good line height and contrast.

### Design &amp;amp; Aesthetics (Crucial)
*   **Theme**: Dark mode. Background should be very dark (nearly black), text should be light grey/white.
*   **Typography**: Use a clean sans-serif font like 'Inter'.
*   **Color Palette**: Use neon/electric accents to pop against the dark background.
    *   *Primary Accent*: Electric Teal or Cyan (for tags/highlights).
    *   *Secondary Accent*: Electric Purple (for the TL;DR section or links).
*   **Visual Style**:
    *   The blog post container should look like a "card" floating in the center of the screen with a subtle shadow and rounded corners.
    *   Use subtle gradients for the text title if possible.
    *   Ensure the design is fully responsive (looks good on mobile).

### Technical Requirements
*   Use Angular Standalone Components.
*   Hardcode all the text content directly in the template or component class for now.
*   Do NOT implement any actual AI calls or backend services; this is purely a UI implementation task.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;At this point, the focus is purely on UI. No AI calls yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the APIs
&lt;/h2&gt;

&lt;p&gt;Create a new service to hold all the AI-related logic.&lt;/p&gt;

&lt;p&gt;Start with a function that creates a session for the Summarization API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createSummarizerSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Summarizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key-points&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expectedInputLanguages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;outputLanguage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expectedContextLanguages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;sharedContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;About AI and Agentic AI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most parameters are self-explanatory. The most important one is &lt;code&gt;type&lt;/code&gt;, which directly influences how the summary is generated.&lt;/p&gt;

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

&lt;p&gt;If you want to explore all available options, check the official documentation:&lt;br&gt;
&lt;a href="https://developer.chrome.com/docs/ai/summarizer-api" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/ai/summarizer-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create a session for the Prompt API. We’ll use it to format the summarizer output into a clean structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createLanguageModelSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;initialPrompts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Convert this bullets into html. Return only the HTML&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, combine both sessions. The summarizer extracts the key points, and the Prompt API enforces a structured output using a schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;generateTlDr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSummarizerSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This article is intended for a tech-savvy audience.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lmSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createLanguageModelSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lmSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;responseConstraint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;lmSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the full service code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Summarizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bullet_list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Identifies the content as a bullet list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;minItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Each entry is one bullet item, without bullet symbols&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AiService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;generateTlDr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSummarizerSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This article is intended for a tech-savvy audience.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lmSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createLanguageModelSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lmSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;responseConstraint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;lmSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createSummarizerSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Summarizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key-points&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;expectedInputLanguages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;outputLanguage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;expectedContextLanguages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;sharedContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;About AI and Agentic AI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createLanguageModelSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;initialPrompts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Convert this bullets into html. Return only the HTML&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, most of the work is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring It into the Component
&lt;/h2&gt;

&lt;p&gt;Call the service from your component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;aiService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AiService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;postContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;tltrContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tldr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateTlDr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tltrContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tldr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update the template to render the TL;DR section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tltr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;TL;DR&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"whitespace-pre-line"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @if (tltrContent().length &amp;gt; 0) {
      &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
        @for (item of tltrContent(); track $index) {
          &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{{ item }}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        }
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
    } @else {
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Loading...&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    }
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TL;DR shown below is real and generated entirely on your machine. No tokens. No cost.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Support and Stability
&lt;/h2&gt;

&lt;p&gt;Some of these APIs are still experimental, while others are already stable. In this example, the Summarization API does most of the heavy lifting and is already stable. The Prompt API is still experimental, but replacing it with regular code later should be straightforward.&lt;/p&gt;

&lt;p&gt;You can find the full example here:&lt;br&gt;
&lt;a href="https://github.com/marianocodes/ai-local-gemini-nano-and-summarize" rel="noopener noreferrer"&gt;https://github.com/marianocodes/ai-local-gemini-nano-and-summarize&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you found this useful, share it and give it a like ❤️.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>webdev</category>
      <category>angular</category>
    </item>
    <item>
      <title>Using AI and Chrome MCP to Automate Core Web Vitals</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Wed, 24 Dec 2025 03:33:42 +0000</pubDate>
      <link>https://dev.to/marianocodes/using-ai-and-chrome-devtools-to-automate-core-web-vitals-56j1</link>
      <guid>https://dev.to/marianocodes/using-ai-and-chrome-devtools-to-automate-core-web-vitals-56j1</guid>
      <description>&lt;p&gt;One of the hardest and most tiring parts of working on web performance is the number of edge cases you need to consider. Some optimizations are purely code-related, while others require running real tests and analyzing the results.&lt;/p&gt;

&lt;p&gt;The main challenge is keeping all of this in mind at the same time. This becomes even harder when you work with a team, where not everyone has the same level of experience or knowledge of performance best practices.&lt;/p&gt;

&lt;p&gt;This is a great use case for AI. You can introduce a step where an LLM runs performance tests, uses ChromeMCP, executes the latest checks, gathers the results, and summarizes them in a way that works for your workflow.&lt;/p&gt;

&lt;p&gt;The key piece here is &lt;strong&gt;ChromeMCP&lt;/strong&gt;, which provides a wide range of functions that allow an LLM to access a page, interact with it, and run Chrome DevTools programmatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompts
&lt;/h2&gt;

&lt;p&gt;The following two prompts are designed to gather performance data and then apply fixes based on what was found.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Core Web Vitals report
&lt;/h3&gt;

&lt;p&gt;This prompt generates a Core Web Vitals report for a specific URL. It performs the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the URL&lt;/li&gt;
&lt;li&gt;Change the network to &lt;code&gt;Slow 4G&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start a performance trace&lt;/li&gt;
&lt;li&gt;Take a screenshot of the page&lt;/li&gt;
&lt;li&gt;Click interactive elements to test INP&lt;/li&gt;
&lt;li&gt;Analyze the results&lt;/li&gt;
&lt;li&gt;Generate the report and close the page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The report is stored in the &lt;code&gt;/reports&lt;/code&gt; directory along with the screenshot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Core Web Vitals Performance Test

Test the page at `&amp;lt;url&amp;gt;` and generate a Core Web Vitals report.

## Instructions

1. **Navigate to the page:**
   - Use `mcp__chrome-devtools__new_page` or `mcp__chrome-devtools__navigate_page` to go to `&amp;lt;url&amp;gt;`

2. **Apply network throttling:**
   - Use `mcp__chrome-devtools__emulate_network` with "Slow 4G" throttling to simulate real-world conditions

3. **Run performance trace:**
   - Use `mcp__chrome-devtools__performance_start_trace` with `reload: true` and `autoStop: true`
   - This will measure LCP, CLS, and other performance metrics

4. **Take a screenshot:**
   - Use `mcp__chrome-devtools__take_screenshot` with `fullPage: true` to capture the page
   - Save it to the `reports/` folder using the `filePath` parameter
   - Name it using the pattern: `screenshot-{domain}-{timestamp}.png`

5. **Test interactions (for INP):**
   - Use `mcp__chrome-devtools__take_snapshot` to identify interactive elements
   - Click on buttons/links using `mcp__chrome-devtools__click` to test interaction responsiveness

6. **Analyze results:**
   - Review the performance trace data
   - Identify LCP, CLS, INP scores
   - Look for performance insights and warnings

7. **Generate report:**
   - Create a markdown file in the `reports/` folder
   - Name it using the pattern: `core-vitals-{domain}-{timestamp}.md`
   - Include all findings, metrics, and actionable recommendations

8. **Clean up:**
   - Use `mcp__chrome-devtools__close_page` to close the browser tab after testing is complete
   - This ensures proper cleanup and prevents memory leaks

## Report Format

The report should be concise and easy to read, including:

- **Test Conditions**: Network throttling, URL tested
- **Core Web Vitals Scores**: LCP, CLS, INP with brief breakdowns
- **Additional Performance Insights**: Key findings from Chrome DevTools
- **Critical Problems Summary**: Top issues prioritized
- **Optimization Opportunities**: High-impact and medium-impact recommendations with estimated improvements

Keep the report focused and actionable. Avoid excessive detail or lengthy code examples.

---

## Example Report

# 🔴 Core Web Vitals Report: /products Page

**Test Conditions:**
- Network: Slow 4G throttling
- URL: https://example.com/products
- Test Date: 2025-01-29 10:30:00 UTC

---

## Core Web Vitals Scores

### 1. LCP (Largest Contentful Paint): 3,245ms 🔴

**Status:** Poor (Target: &amp;lt; 2,500ms for "Good")

**LCP Element:** Hero banner image

**Breakdown:**
- TTFB (Time to First Byte): 850ms ⚠️
- Load Delay: 1,200ms 🔴 (37% of LCP - time before image starts loading)
- Load Duration: 450ms ⚠️ (actual download time)
- Render Delay: 745ms ⚠️ (23% of LCP - time after download before paint)

**Key Issues:**
- Hero image is not discoverable immediately from HTML
- No preload hint for critical image
- Image served at 4MB uncompressed (3000x2000px)
- No modern image format (WebP/AVIF)

---

### 2. CLS (Cumulative Layout Shift): 0.25 🔴

**Status:** Poor (Target: &amp;lt; 0.1)

**Major Shifts:**
1. Hero image load: 0.15 shift (no width/height attributes)
2. Ad insertion: 0.08 shift (dynamic content injection)
3. Web font loading: 0.02 shift (FOUT)

**Key Issues:**
- Images missing explicit dimensions
- Dynamic content inserted without reserved space
- Fonts loaded asynchronously causing text reflow

---

### 3. INP (Interaction to Next Paint): 580ms 🔴

**Tested Interaction:** "Add to Cart" button click

**Status:** Poor (Target: &amp;lt; 200ms)

**Key Issues:**
- Heavy synchronous JavaScript execution on click (450ms)
- Main thread blocked during state update
- No visual feedback during processing
- Long tasks preventing quick response

---

## Additional Performance Insights

### Render Blocking Resources
- 3 CSS files blocking render (total 245KB)
- 2 JavaScript bundles blocking initial paint (total 890KB)
- **Estimated savings:** 1,200ms on LCP

### Network Dependency Chain
- Long chain of sequential resource requests (5 levels deep)
- Hero image depends on CSS → JS → API call

### Third-Party Impact
- Google Analytics: 180ms execution time
- Facebook Pixel: 220ms execution time

---

## Critical Problems Summary

1. **LCP Problem (3,245ms):**
   - 1,200ms load delay + 745ms render delay = 1,945ms of preventable delay
   - Image optimization and preloading are critical

2. **CLS Problem (0.25):**
   - Missing image dimensions causing major layout shifts
   - Dynamic content injection without space reservation

3. **INP Problem (580ms):**
   - Synchronous blocking operation prevents quick interaction response
   - Violates the 200ms INP threshold for good user experience

---

## Optimization Opportunities

### High Impact:
- Convert hero image to WebP/AVIF and compress to &amp;lt; 200KB
- Add `fetchpriority="high"` and preload hint to hero image
- Add explicit width/height to all images
- Use non-blocking state updates for button interactions
- **Estimated LCP improvement:** &amp;lt; 1,500ms (54% faster)
- **Estimated CLS improvement:** &amp;lt; 0.05 (80% better)
- **Estimated INP improvement:** &amp;lt; 180ms (69% faster)

### Medium Impact:
- Implement code splitting for JavaScript bundles
- Defer non-critical third-party scripts
- Reserve space for dynamic content (ads, widgets)
- Optimize font loading strategy
- Enable Brotli/Gzip compression

---

**Report generated:** 2025-01-29 10:30:00 UTC

---

## Usage

Replace `&amp;lt;url&amp;gt;` with the actual URL to test:

Test the page at http://localhost:3000/unoptimized and generate a comprehensive Core Web Vitals report.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix the issues found in the report
&lt;/h3&gt;

&lt;p&gt;Once you have the report, you know exactly what needs to be fixed. The prompt below takes the report as part of the context and attempts to apply the recommended improvements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Optimize Code for Core Web Vitals

Read the Core Web Vitals report at `&amp;lt;path&amp;gt;` and implement the recommended optimizations to improve performance.

## Instructions

1. **Read the report:**
   - Use the Read tool to load the report file at `&amp;lt;path&amp;gt;`
   - Identify all issues and recommendations
   - Prioritize high-impact optimizations first

2. **Analyze the page:**
   - Identify the files that need to be modified
   - Use Glob and Grep tools to find relevant code
   - Read the current implementation

3. **Implement optimizations:**
   - Start with high-impact fixes (LCP, then CLS, then INP)
   - Make code changes using the Edit tool
   - Add clear comments explaining each optimization
   - Follow the recommendations from the report

4. **Common optimizations to implement:**

   **For LCP Issues:**
   - Convert `&amp;lt;img&amp;gt;` tags to Next.js `Image` component
   - Add `priority={true}` to hero/above-fold images
   - Add explicit `width` and `height` props
   - Set appropriate `quality` (e.g., 75 instead of 100)
   - Add responsive `sizes` attribute

   **For CLS Issues:**
   - Add explicit dimensions to all images
   - Add `aspect-ratio` CSS or container dimensions
   - Reserve space for dynamic content
   - Optimize font loading with `font-display`

   **For INP Issues:**
   - Replace blocking operations with async/non-blocking code
   - Use React's `useTransition` for state updates
   - Add loading states for immediate user feedback
   - Move heavy computations to Web Workers or defer them

   **General Optimizations:**
   - Add lazy loading (`loading="lazy"`) to below-fold images
   - Implement code splitting where appropriate
   - Defer non-critical scripts
   - Add preload hints for critical resources

5. **Document changes:**
   - Create a summary document in `reports/`
   - Name it: `optimization-summary-{timestamp}.md`
   - List all files modified
   - Describe each change made
   - Include expected performance impact

6. **Verify changes:**
   - Ensure no syntax errors were introduced
   - Check that imports are correct
   - Verify that the code follows best practices

## Optimization Guidelines

- **Be precise:** Only modify the code that needs optimization
- **Add comments:** Explain why each optimization was made
- **Follow patterns:** Use existing code style and conventions
- **Test safety:** Don't break existing functionality
- **High impact first:** Prioritize optimizations with the biggest performance gains

## Example Usage

Read the Core Web Vitals report at reports/core-vitals-localhost-1738166400.md and implement the recommended optimizations to improve performance.


---

## Example Optimization Summary

# Core Web Vitals Optimization Summary

**Date:** 2025-01-29 14:30:00 UTC
**Report Used:** reports/core-vitals-example-1738166400.md
**Page Optimized:** /products

---

## Issues Identified from Report

Based on the report analysis, the following high-priority issues were found:

1. **LCP (3,245ms):** Hero image not optimized, no preload hints
2. **CLS (0.25):** Missing image dimensions, dynamic content shifts
3. **INP (580ms):** Blocking JavaScript execution on button click

---

## Changes Made

### 1. LCP Optimization - Hero Image

**File:** `app/products/page.tsx` (lines 45-52)

**Issue from report:**
- Hero image served at 4MB uncompressed
- No preload hint for critical image
- Image not discoverable from HTML immediately

**Changes applied:**
- [Specific changes based on report recommendations]
- [Added necessary props/attributes]
- [Modified relevant code sections]

**Expected Impact:** -1,200ms on LCP

---

### 2. CLS Optimization - Image Dimensions

**File:** `app/products/page.tsx` (lines 78-120)

**Issue from report:**
- Images missing explicit dimensions causing 0.15 shift
- Dynamic ad insertion causing 0.08 shift

**Changes applied:**
- [Specific changes based on report recommendations]
- [Added dimension specifications]
- [Reserved space for dynamic content]

**Expected Impact:** -0.23 CLS (from 0.25 to 0.02)

---

### 3. INP Optimization - Button Interactions

**File:** `app/products/page.tsx` (lines 156-168)

**Issue from report:**
- Heavy synchronous JavaScript execution (450ms)
- Main thread blocked during state update

**Changes applied:**
- [Specific changes based on report recommendations]
- [Refactored blocking code]
- [Added non-blocking patterns]

**Expected Impact:** -400ms on INP (from 580ms to ~180ms)

---

## Files Modified

- `app/products/page.tsx` - Optimized images and interactions
- [Additional files if needed]

---

## Expected Performance Results

### Before Optimization (from report):
- LCP: 3,245ms 🔴
- CLS: 0.25 🔴
- INP: 580ms 🔴

### After Optimization (estimated):
- LCP: ~2,045ms ⚠️ (37% improvement)
- CLS: ~0.02 ✅ (92% improvement)
- INP: ~180ms ✅ (69% improvement)

**Note:** Run a new performance test to verify actual improvements.

---

**Optimization completed:** 2025-01-29 14:30:00 UTC

---

## Important Notes

- Always read the entire report before making changes
- Focus on high-impact optimizations first (those with largest expected improvements)
- Test after each major change to ensure nothing broke
- Keep the original unoptimized version for comparison
- Add detailed comments to explain each optimization
- If the report mentions issues that don't apply to the current code, skip them
- Don't over-optimize - focus on the specific issues identified in the report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tips and ideas to improve your development flow
&lt;/h2&gt;

&lt;p&gt;These prompts can be used with any AI tool such as GeminiCLI, Claude Code, Cursor, or Antigravity. You will need to adapt and tweak them depending on the tool and setup you are using.&lt;/p&gt;

&lt;p&gt;Some ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create custom slash commands in GeminiCLI and pass the URL directly, for example: &lt;code&gt;@test-core-vitals url:/products&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a sub-agent in Claude Code and call it in a similar way. The AI is smart enough to understand that &lt;code&gt;url&lt;/code&gt; is a variable and replace it in the context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a Git hook that triggers these prompts automatically to detect performance issues and apply fixes early.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The optimization prompt is focused on React applications, but it can be adapted to any framework. For Angular projects, you can combine this approach with the &lt;a href="https://dev.to/marianocodes/reduce-hallucinations-when-working-on-a-angular-and-gemini-cli-42da"&gt;Angular MCP&lt;/a&gt; to get better and more consistent results.&lt;/p&gt;




&lt;p&gt;If you want to see more of my content, follow me on &lt;a href="https://www.linkedin.com/in/marianocodes/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don't forget to like and share ❤️&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webperf</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Avoiding Hallucinations When Building Angular Apps with Gemini CLI</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Tue, 23 Dec 2025 05:28:12 +0000</pubDate>
      <link>https://dev.to/marianocodes/reduce-hallucinations-when-working-on-a-angular-and-gemini-cli-42da</link>
      <guid>https://dev.to/marianocodes/reduce-hallucinations-when-working-on-a-angular-and-gemini-cli-42da</guid>
      <description>&lt;p&gt;We are seeing new models released every five or six months. That does not mean they are trained with the latest information. For example, a model may not include the most recent Angular documentation and can easily generate outdated code that does not follow current best practices.&lt;/p&gt;

&lt;p&gt;GeminiCLI is a great tool for building faster with AI, but it is not immune to hallucinations. Below are a few techniques you can use to get more accurate output and better-aligned Angular code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt with best practices
&lt;/h2&gt;

&lt;p&gt;You can define a shared prompt in your Gemini.md file. This helps guide the model to generate code that follows modern Angular patterns and current best practices.&lt;/p&gt;

&lt;p&gt;Another option is to define a custom slash command and trigger it with every request. That way, you make sure your instructions are always included.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an expert in TypeScript, Angular, and scalable web application development. You write functional, maintainable, performant, and accessible code following Angular and TypeScript best practices.

## TypeScript Best Practices
- Use strict type checking
- Prefer type inference when the type is obvious
- Avoid the `any` type; use `unknown` when the type is uncertain

## Angular Best Practices
- Always use standalone components over NgModules
- Must NOT set `standalone: true` inside Angular decorators. It is the default in Angular v20+
- Use signals for state management
- Implement lazy loading for feature routes
- Do NOT use `@HostBinding` or `@HostListener`. Use the `host` object in the `@Component` or `@Directive` decorator instead
- Use `NgOptimizedImage` for all static images
  - `NgOptimizedImage` does not work with inline base64 images

## Accessibility Requirements
- Must pass all AXE checks
- Must follow WCAG AA minimums, including focus management, color contrast, and proper ARIA attributes

## Components
- Keep components small and focused on a single responsibility
- Use `input()` and `output()` functions instead of decorators
- Use `computed()` for derived state
- Set `changeDetection: ChangeDetectionStrategy.OnPush` in the `@Component` decorator
- Prefer inline templates for small components
- Prefer reactive forms over template-driven forms
- Do NOT use `ngClass`; use `class` bindings instead
- Do NOT use `ngStyle`; use `style` bindings instead
- When using external templates or styles, use paths relative to the component TypeScript file

## State Management
- Use signals for local component state
- Use `computed()` for derived state
- Keep state transformations pure and predictable
- Do NOT use `mutate` on signals; use `update` or `set` instead

## Templates
- Keep templates simple and avoid complex logic
- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
- Use the async pipe to handle observables
- Do not assume globals like `new Date()` are available
- Do not write arrow functions in templates

## Services
- Design services around a single responsibility
- Use `providedIn: 'root'` for singleton services
- Use the `inject()` function instead of constructor injection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach gives you full control over the rules you want the model to follow. The downside is that this file becomes another artifact you need to keep up to date as Angular evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  llms.txt
&lt;/h2&gt;

&lt;p&gt;Another approach is exposing a llms.txt file. The idea is for websites to provide a single entry point that language models can use to discover official and structured documentation. Conceptually, it is similar to a RAG setup, but without the infrastructure overhead.&lt;/p&gt;

&lt;p&gt;Angular currently exposes two versions:&lt;br&gt;
• &lt;a href="https://angular.dev/llms.txt" rel="noopener noreferrer"&gt;llms.txt&lt;/a&gt; A lightweight file that links to key resources&lt;br&gt;
• &lt;a href="https://angular.dev/llms-full.txt" rel="noopener noreferrer"&gt;llms-full.txt&lt;/a&gt; A comprehensive set of resources that explains how Angular works and how to build Angular applications&lt;/p&gt;

&lt;p&gt;This solves part of the maintenance problem because you no longer need to keep your own prompt updated. The tradeoff is that you now depend on an external source being available and accurate.&lt;/p&gt;
&lt;h2&gt;
  
  
  Angular MCP
&lt;/h2&gt;

&lt;p&gt;The Angular CLI now includes an MCP that GeminiCLI can consume to access up-to-date documentation and best practices. This is the most robust option and requires very little setup.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;.gemini/settings.json&lt;/code&gt;, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mcpServers": {
    "angular-cli": {
      "command": "npx",
      "args": ["-y", "@angular/cli", "mcp"]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives GeminiCLI access to several Angular-specific tools:&lt;br&gt;
• &lt;code&gt;ai_tutor&lt;/code&gt;&lt;br&gt;
Launches an interactive Angular tutor. Recommended for new Angular v20+ projects&lt;br&gt;
• &lt;code&gt;find_examples&lt;/code&gt;&lt;br&gt;
Finds authoritative and up-to-date code examples based on official Angular sources&lt;br&gt;
• &lt;code&gt;get_best_practices&lt;/code&gt;&lt;br&gt;
Retrieves the Angular Best Practices Guide, covering standalone components, typed forms, and modern control flow&lt;br&gt;
• &lt;code&gt;list_projects&lt;/code&gt;&lt;br&gt;
Lists all applications and libraries defined in an Angular workspace by reading angular.json&lt;br&gt;
• &lt;code&gt;onpush_zoneless_migration&lt;/code&gt;&lt;br&gt;
Analyzes your codebase and provides a step-by-step plan to migrate to OnPush change detection, a requirement for zoneless applications&lt;br&gt;
• &lt;code&gt;search_documentation&lt;/code&gt;&lt;br&gt;
Searches the official Angular documentation at angular.dev for APIs, guides, and best practices&lt;/p&gt;

&lt;p&gt;If you have not tried GeminiCLI yet, it is worth a look. It is simple to use and surprisingly powerful when paired with the right context.&lt;/p&gt;

&lt;p&gt;-- &lt;/p&gt;

&lt;p&gt;Don't forget to like and share! ❤️&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>ai</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Use Your ADK AI Agent in a UI</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Mon, 30 Jun 2025 02:13:35 +0000</pubDate>
      <link>https://dev.to/marianocodes/use-your-adk-ai-agent-in-a-ui-4bn8</link>
      <guid>https://dev.to/marianocodes/use-your-adk-ai-agent-in-a-ui-4bn8</guid>
      <description>&lt;p&gt;In the last post, we created an agent with memory using ADK and ran it through the dev dashboard.&lt;/p&gt;

&lt;p&gt;Cool. But… now what?&lt;/p&gt;

&lt;p&gt;You probably want to use that agent outside of the testing UI — like inside your own app. Let’s walk through how to serve the agent with FastAPI and connect it from a front-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your agent
&lt;/h2&gt;

&lt;p&gt;Start by organizing your project like this:&lt;/p&gt;

&lt;p&gt;Then, in a file called agent.py, define your agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="n"&gt;root_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;costa_rica_expert_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-2.0-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent to answer questions about Costa Rica and its culture&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful agent who can answer user questions about Costa Rica and its culture&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; You need to name your agent variable root_agent. ADK looks for that name specifically — if it’s called anything else, it won’t work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your server
&lt;/h2&gt;

&lt;p&gt;Now let’s wire it up with FastAPI.&lt;/p&gt;

&lt;p&gt;Here’s what your main.py might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvicorn&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.cli.fast_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_fast_api_app&lt;/span&gt;

&lt;span class="n"&gt;AGENT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;SESSION_DB_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlite:///memory.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ALLOWED_ORIGINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8080&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;SERVE_WEB_INTERFACE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_fast_api_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agents_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AGENT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;session_service_uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SESSION_DB_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allow_origins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ALLOWED_ORIGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SERVE_WEB_INTERFACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PORT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the things I like most about ADK — they give you a helper to set up the whole FastAPI app in one line. No need to wire everything manually.&lt;/p&gt;

&lt;p&gt;And if you want to add your own routes, just treat it like any FastAPI app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then just run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python main.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You’re live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend integration
&lt;/h2&gt;

&lt;p&gt;If you’re building your UI in Angular (like I am), here’s how you can connect to the agent.&lt;/p&gt;

&lt;p&gt;ADK’s dev dashboard is open source and built with Angular. Here’s how it makes API calls to the agent using Server-Sent Events (SSE):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;runSse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AgentRunRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiServerDomain&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;`/run_sse`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="nx"&gt;lastData&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\r?\n&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
              &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^data:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
              &lt;span class="p"&gt;});&lt;/span&gt;
              &lt;span class="nx"&gt;lastData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// wait for the next chunk&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// keep reading&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah — not exactly simple.&lt;/p&gt;

&lt;p&gt;Even if you don’t want streaming, the /run_sse endpoint always returns an event-stream. So you still have to handle it this way for now.&lt;/p&gt;

&lt;p&gt;If you want to check it out directly, here’s the link to the actual implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can this be easier?
&lt;/h2&gt;

&lt;p&gt;Definitely.&lt;/p&gt;

&lt;p&gt;In the next post, I’ll show you how to simplify this and connect to your agent from any front-end — without needing to manually handle SSE streams.&lt;/p&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;

&lt;p&gt;And if this helped, a like or comment goes a long way.&lt;/p&gt;

</description>
      <category>adk</category>
      <category>ai</category>
      <category>angular</category>
      <category>agents</category>
    </item>
    <item>
      <title>Adding Sessions and Memory to Your AI Agent with Agent Development Kit (ADK)</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Sun, 29 Jun 2025 16:42:54 +0000</pubDate>
      <link>https://dev.to/marianocodes/adding-sessions-and-memory-to-your-ai-agent-with-agent-development-kit-adk-31ap</link>
      <guid>https://dev.to/marianocodes/adding-sessions-and-memory-to-your-ai-agent-with-agent-development-kit-adk-31ap</guid>
      <description>&lt;p&gt;In my previous post, I walked through how to build a basic AI agent using ADK. It’s exciting to get an agent running and see it respond to prompts — but once the session ends, that context is gone. It has no memory of anything that happened before.&lt;/p&gt;

&lt;p&gt;This is fine for demos, but if you’re aiming to build something real then you’ll quickly hit a wall.&lt;/p&gt;

&lt;p&gt;Fortunately, ADK gives us tools to fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Sessions Matter
&lt;/h2&gt;

&lt;p&gt;Let’s say you build a ticket finder agent that helps someone search for flights. During the conversation, the user mentions that they prefer Delta and want to fly out of San Francisco.&lt;/p&gt;

&lt;p&gt;Without session management, the agent forgets all of that context between interactions. So even if the user follows up with, “What about something cheaper?” the agent has no clue what they’re referring to.&lt;/p&gt;

&lt;p&gt;That’s where Session comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Session?
&lt;/h2&gt;

&lt;p&gt;In ADK, a Session is an object that represents a conversation. It keeps track of who the user is, what app (agent) is running, what’s been said, what tools were called, and any custom state you want to track along the way.&lt;/p&gt;

&lt;p&gt;Each session includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic identifiers&lt;/strong&gt;&lt;br&gt;
    • id: A unique session ID&lt;br&gt;
    • appName: Name of the agent application&lt;br&gt;
    • userId: The user having the conversation&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Events&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A chronological list of everything that happens — user messages, tool calls, agent replies, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A dictionary that stores structured data the agent can access and modify during the session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;lastUpdateTime&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Timestamp for the latest interaction in the session.&lt;/p&gt;
&lt;h3&gt;
  
  
  Choosing the Right SessionService
&lt;/h3&gt;

&lt;p&gt;To use sessions, you need a session service. ADK gives you a few out-of-the-box options, depending on your environment:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. InMemorySessionService&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is great for local development or protyping. Everything is stored in memory — and wiped as soon as the app restarts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.sessions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InMemorySessionService&lt;/span&gt;

&lt;span class="n"&gt;session_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InMemorySessionService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. DatabaseSessionService&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This option is for when you’re running something in production. It stores sessions in a relational database and uses a built-in migration system to manage the schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.sessions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DatabaseSessionService&lt;/span&gt;

&lt;span class="n"&gt;db_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_database_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;session_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DatabaseSessionService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;db_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A heads-up:&lt;/strong&gt; ADK manages the database schema internally, so if you’re integrating this with your existing database, I strongly recommend using a dedicated schema with a separate user that has limited access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jppaftqj4mpmlnbhsvz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jppaftqj4mpmlnbhsvz.png" alt="Screenshot taken from the repo" width="588" height="812"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. VertexAiSessionService&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re on Google Cloud and want tight integration with Vertex AI, you can use this session service to leverage Google’s managed infrastructure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.sessions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VertexAiSessionService&lt;/span&gt;

&lt;span class="n"&gt;session_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiSessionService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-gcp-project-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-central1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a Session
&lt;/h2&gt;

&lt;p&gt;Once your session service is initialized, you can create a new session like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticket_finder_app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;marianocodes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;preferred_airline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll get back a session object that you can attach events to, store state in, and use throughout the conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Session State
&lt;/h2&gt;

&lt;p&gt;The state object is a dictionary you can use to store anything your agent should remember while the session is active.&lt;/p&gt;

&lt;p&gt;If you’re a frontend developer, think of it like a Redux store scoped to the conversation. If you’re coming from a backend background, you can think of it like a session object tied to a user’s request — only it’s more flexible.&lt;/p&gt;

&lt;h2&gt;
  
  
  State Guidelines
&lt;/h2&gt;

&lt;p&gt;A few things to keep in mind:&lt;br&gt;
    1. Use primitives — strings, numbers, booleans. Don’t store complex objects or custom classes.&lt;br&gt;
    2. Scope your data using prefixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No prefix&lt;/strong&gt; &lt;code&gt;session.state["last_message"] = "hello"&lt;/code&gt; visible only during this session&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;user:&lt;/strong&gt; &lt;code&gt;session.state["user:theme"] = "dark"&lt;/code&gt; shared across all of the user’s sessions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;app:&lt;/strong&gt; &lt;code&gt;session.state["app:language"] = "en"&lt;/code&gt; shared across the app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;temp:&lt;/strong&gt; &lt;code&gt;session.state["temp:step"] = "waiting_for_payment"&lt;/code&gt; temporary info, not persisted long-term&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How to Update State
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to update the session state, depending on the structure of your agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Via output_key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the simplest method. If your agent returns a value and you want to store it automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ticket_finder_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ticket Finder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-2.5-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;best_ticket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whatever value is returned from the model will be stored under session.state["best_ticket"].&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Via ToolContext or CallbackContext&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside tool or callback functions, you get access to the session’s context. You can use that to manually update state at any point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;purchase_ticket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ToolContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;waiting_for_purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when you want to track a specific action or condition and update state based on logic inside your tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Using EventActions.state_delta&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This gives you full control — you define what changes in the state and register that as a system-level event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;state_changes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticket_in_screen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AV-258&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EventActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_delta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;state_changes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;invocation_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inv_state_update&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s more verbose, but also more precise when you want to track a series of changes or attach metadata.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Memory
&lt;/h2&gt;

&lt;p&gt;Sessions let your agent remember context during a conversation. But what about after the session ends?&lt;/p&gt;

&lt;p&gt;That’s where memory comes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;InMemoryMemoryService&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This lets your agent access information during a single session — similar to &lt;code&gt;InMemorySessionService&lt;/code&gt; but not the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VertexAiRagMemoryService (GCP)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ADK includes a memory service built on RAG (retrieval-augmented generation). It stores content as embeddings and lets your agent retrieve relevant context across sessions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.memory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VertexAiRagMemoryService&lt;/span&gt;

&lt;span class="n"&gt;memory_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiRagMemoryService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;rag_corpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;projects/your-gcp-project-id/locations/us-central1/ragCorpora/your-corpus-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;similarity_top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vector_distance_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use it inside an agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_memory&lt;/span&gt;

&lt;span class="n"&gt;ticket_finder_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ticket Finder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-2.5-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;load_memory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, once the session is done, you can store its content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;APP_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;USER_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;memory_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_session_to_memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What If You’re Not Using GCP?
&lt;/h2&gt;

&lt;p&gt;I got you, You can still build something similar.&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;load_memory&lt;/code&gt; just performs a similarity search using Vertex AI. You can replicate this using your own vector DB (like Pinecone, Qdrant, or AstraDB). You just need to handle chunking, embedding, and retrieval on your own.&lt;/p&gt;

&lt;p&gt;Here’s a peek at what the function looks like behind the scenes:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogjx388vk8hnf21f6ms5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogjx388vk8hnf21f6ms5.png" alt="Definition of function called in load_more function" width="586" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;We’ve covered how to set up an agent — and now, how to give it memory. But so far, everything’s only been accessible through the testing UI.&lt;/p&gt;

&lt;p&gt;What if you want to run it in a real web app?&lt;/p&gt;




&lt;p&gt;Want to support my work? A quick like goes a long way ❤️.&lt;/p&gt;

</description>
      <category>adk</category>
      <category>ai</category>
      <category>agents</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Lessons from 6 Months of Building AI Agents</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Fri, 20 Jun 2025 16:49:02 +0000</pubDate>
      <link>https://dev.to/marianocodes/lessons-from-6-months-of-building-ai-agents-2c96</link>
      <guid>https://dev.to/marianocodes/lessons-from-6-months-of-building-ai-agents-2c96</guid>
      <description>&lt;p&gt;Over the past six months, I’ve been deep in the trenches working with AI agents. I’ve built prototypes, tested frameworks, broken things, and occasionally gotten something to work the way I intended.&lt;/p&gt;

&lt;p&gt;It’s been exciting — but also humbling.&lt;/p&gt;

&lt;p&gt;There’s a lot of hype out there, and while much of it is grounded in genuine progress, I’ve seen first-hand how different the day-to-day reality is from the glossy demos and blog posts.&lt;/p&gt;

&lt;p&gt;If you’re getting started with AI agents or just curious about what it’s really like, here are eight honest takeaways from my experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Most “AI Agents” Aren’t Really Agents&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You’ve probably seen demos of AI agents that can schedule appointments, handle customer support, or generate content for your brand. They look polished. They seem smart. But here’s the truth:&lt;/p&gt;

&lt;p&gt;Most of these are just well-structured workflows with a bit of LLM magic sprinkled in.&lt;/p&gt;

&lt;p&gt;They aren’t making real decisions. They’re running predefined steps — like “if the message includes a time, call the calendar API” — and using the LLM only to interpret input or format output. That’s automation, not agency.&lt;/p&gt;

&lt;p&gt;If you’re expecting autonomy, judgment, or adaptation, you’ll be disappointed — unless you build it yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. We’re Early. Really Early.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There’s a shared moment a lot of people have when they first dive into AI: “This is going to replace us.”&lt;/p&gt;

&lt;p&gt;And maybe it will, someday. But not today.&lt;/p&gt;

&lt;p&gt;As developers, we already know where AI can help right now — drafting code, writing tests, summarizing input. But there are still major limitations in reasoning, context retention, memory, and reliability.&lt;/p&gt;

&lt;p&gt;AI agents aren’t anywhere close to replacing full-time employees in most domains. There’s a long road ahead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Happy Path Isn’t Enough&lt;/strong&gt;&lt;br&gt;
Almost every tutorial or demo out there shows the same types of agents: the travel assistant, the research bot, the meeting note taker.&lt;/p&gt;

&lt;p&gt;They’re helpful — but they only show the “happy path.” The scenario where everything works, every tool returns a valid result, and the user asks perfect questions.&lt;/p&gt;

&lt;p&gt;Building real agents means dealing with broken APIs, vague input, long-running tasks, and dead ends. The real world is messy — your agents need to be prepared for that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Agents Are Slow (and That’s a Problem)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest performance surprises when building agents is how slow they can be.&lt;/p&gt;

&lt;p&gt;You’re likely calling an LLM multiple times, invoking tools or APIs, chaining results, and possibly routing through sub-agents. Each step adds latency.&lt;/p&gt;

&lt;p&gt;Unlike ChatGPT — where billions have been spent on speed and optimization — your custom agent lives in your infra. And speed will matter to your users.&lt;/p&gt;

&lt;p&gt;If you want the experience to feel responsive, you’ll need to optimize around latency: infrastructure, parallelism, UI tricks like optimistic rendering — whatever you can do to mask the delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Tooling Is Easy. Prompting Is Hard.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing the code to call a tool? Easy.&lt;/p&gt;

&lt;p&gt;Getting your agent to consistently decide when to call that tool — and how to use it — based on messy, real-world inputs? Not so easy.&lt;/p&gt;

&lt;p&gt;Prompts are where most of the real work happens. They’re fragile. Small wording changes can completely shift the model’s behavior. And you’ll spend a lot of time iterating, debugging, and rephrasing.&lt;/p&gt;

&lt;p&gt;It can be frustrating. But when you finally get a clean, natural response chain — it’s worth it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Modularity Comes at a Cost&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As engineers, we’re trained to decompose problems into smaller units — functions, services, components.&lt;/p&gt;

&lt;p&gt;It’s tempting to do the same with agents: one for scheduling, one for research, one for recommendations, and a router that directs traffic.&lt;/p&gt;

&lt;p&gt;That works, but it introduces two big problems:&lt;br&gt;
    • Context fragmentation: It becomes harder to maintain state across multiple agents.&lt;br&gt;
    • Latency overhead: Every routing decision is another round-trip with the model or server.&lt;/p&gt;

&lt;p&gt;Sometimes, fewer agents with more tightly scoped memory works better than trying to modularize everything too early.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Prompting &amp;gt; Coding (and That’s the Job)&lt;/strong&gt;&lt;br&gt;
If you want to get good at building agents, you need to get good at writing prompts.&lt;/p&gt;

&lt;p&gt;Use Gemini, ChatGPT, or your LLM of choice to help generate and improve them. But don’t blindly trust the output. Always read it. Test it. Make sure the behavior aligns with what you intended.&lt;/p&gt;

&lt;p&gt;Too many devs treat LLMs like a black box that “just works.” That leads to brittle systems. If you’re not reading and refining your prompts, you’re not really building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Frameworks Matter, but Start Simple&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first experience building agents was with LangGraph. It’s powerful and flexible — but also low-level and hard to grasp at first. The documentation isn’t always clear, and there are often multiple ways to solve the same problem.&lt;/p&gt;

&lt;p&gt;If you’re just getting started, pick a simpler tool and focus on getting something working.&lt;/p&gt;

&lt;p&gt;In my case, I eventually moved to Google’s Agent Development Kit (ADK). It’s easy to use, supports multi-agent flows, tool integration, memory, and works great with Google’s infrastructure like MCP and A2A.&lt;/p&gt;

&lt;p&gt;I’ve used it since the early versions and reported a few bugs — the team behind it is active and responsive, and the framework has improved fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building AI agents is hard. It’s not magic. It’s not plug-and-play. But it is possible — and incredibly satisfying when it works.&lt;/p&gt;

&lt;p&gt;The biggest unlock isn’t the model. It’s how you design your prompts, your tools, your UX, and your system architecture to work with the model — not against it.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I’d love to hear your perspective:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What lessons have you learned while building or exploring AI agents? What’s worked — and what hasn’t?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>adk</category>
      <category>langgraph</category>
      <category>agents</category>
    </item>
    <item>
      <title>Build your first AI Agent with ADK - Agent Development Kit by Google</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Sun, 01 Jun 2025 23:22:48 +0000</pubDate>
      <link>https://dev.to/marianocodes/build-your-first-ai-agent-with-adk-agent-development-kit-by-google-409b</link>
      <guid>https://dev.to/marianocodes/build-your-first-ai-agent-with-adk-agent-development-kit-by-google-409b</guid>
      <description>&lt;p&gt;I’ve been working with AI agents since the beginning of the year. It hasn’t been an easy journey — there was a ton of learning and experimentation along the way.&lt;/p&gt;

&lt;p&gt;Throughout this process, I tried different libraries and frameworks. For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LangGraph&lt;/strong&gt;: It's a great tool for building low-level flows. Getting a simple flow working is easy, but things quickly get complex when you start connecting more than one node. Another issue I found is the documentation — it’s confusing, as there are several ways to define an agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agno&lt;/strong&gt;: I really liked this one. The API is simple and easy to understand, and the documentation is great. However, when I tried to set up a multi-agent architecture, I ran into performance issues.&lt;/p&gt;

&lt;p&gt;But then I tried &lt;strong&gt;ADK (Agent Development Kit)&lt;/strong&gt; — and it solved most of my problems. It has great documentation, it’s backed by Google, and the team seems very active, delivering fixes weekly. I was able to understand everything very quickly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Movie Finder with ADK
&lt;/h2&gt;

&lt;p&gt;ADK works with Python, and there’s also a recent version available in Java. For this example, we’ll stick with Python. Let’s get started:&lt;/p&gt;

&lt;p&gt;Install the package:&lt;code&gt;pip install google-adk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Set up your environment keys — in this case, we’ll use Gemini. You can generate the API key in Google AI Studio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GOOGLE_GENAI_USE_VERTEXAI=FALSE
GOOGLE_API_KEY=PASTE_YOUR_ACTUAL_API_KEY_HERE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create the folder structure for your agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parent_folder/
    movie_finder_agent/
        __init__.py
        agent.py
        .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;__init__.py&lt;/code&gt;, make sure to include &lt;code&gt;from . import agent&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now let’s work on the tool our agent will use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MOVIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The Matrix&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sci-Fi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1999&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Inception&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sci-Fi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Titanic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Romance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1997&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The Godfather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Crime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1972&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Interstellar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sci-Fi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2014&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pulp Fiction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Crime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1994&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_movies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Find movies that match the specified genre and year.

    Args:
        genre (str): The genre to filter movies by (e.g. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sci-Fi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Romance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Crime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)
        decade (int): The start year of the decade to filter movies by (e.g. 1990, 2000)

    Returns:
        str: A newline-separated string of matching movie titles, or a message if no matches found
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;    
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;year_range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decade&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decade&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;MOVIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;year_range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;          

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No matching movies found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, a tool is just a regular function the agent can call — nothing new here. In this case, the agent’s job is to figure out the parameters the tool needs in order to run correctly.&lt;/p&gt;

&lt;p&gt;Here’s how the agent is defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;root_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;root_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-2.5-flash-preview-04-17&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Answers questions from the user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful assistant that can answer questions about movies.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;find_movies&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s very simple to understand, but let me break down the properties:&lt;br&gt;
    -   name – the agent’s name&lt;br&gt;
    -   model – the model version; if you want to use a non-Google model, you can use LiteLLM&lt;br&gt;
    -   description – a short summary of what the agent does&lt;br&gt;
    -   instruction – your agent’s prompt and behavior guide&lt;br&gt;
    -   tools – the list of tools your agent can call&lt;/p&gt;

&lt;p&gt;Now let’s see it in action. Run &lt;code&gt;adk web&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;This dashboard is the testing environment, and here you can see the agent in action, and as you can see I asked: &lt;code&gt;I'm looking for a sci-fi movie in the 90s.&lt;/code&gt; It correctly used the tool and generated the final response.&lt;/p&gt;

&lt;p&gt;If we inspect the tool call, we can see the correct parameters were passed:&lt;/p&gt;

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

&lt;p&gt;And it returned the result as expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjiawvzlh1sqj1scivqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjiawvzlh1sqj1scivqh.png" alt="Results from the tool call" width="497" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In under 5 minutes, you created your first agent — with minimal effort and simple prompts!&lt;/p&gt;

&lt;h3&gt;
  
  
  Agents Are Not Just LLMs
&lt;/h3&gt;

&lt;p&gt;It’s important to understand a few key concepts to avoid confusion or wrong expectations when working with agents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LLM ≠ Agent&lt;/strong&gt; An LLM is a pretrained model built using massive datasets, high compute costs, and long training cycles. An agent is a runtime layer built on top of an LLM that adds tools, memory, and logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; can think, remember, and act
They can analyze a question, call a tool to get an answer, and store that result for future use — like a lightweight brain with short-term memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; are slower than chat apps
Don’t expect the speed you see in ChatGPT or Gemini.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;If you want a part 2 of this tutorial, leave a like!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>google</category>
      <category>llm</category>
    </item>
    <item>
      <title>Cómo Utilizar el API Experimental de AI en Chrome</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Tue, 01 Oct 2024 03:44:18 +0000</pubDate>
      <link>https://dev.to/marianocodes/como-utilizar-el-api-experimental-de-ai-en-chrome-3c4m</link>
      <guid>https://dev.to/marianocodes/como-utilizar-el-api-experimental-de-ai-en-chrome-3c4m</guid>
      <description>&lt;p&gt;&lt;em&gt;Historial de cambios:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;25/10/24 Edición: se actualizo el API, cambio de &lt;code&gt;ai.assistant&lt;/code&gt; a &lt;code&gt;ai.languageModel&lt;/code&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Para utilizar el API experimental de AI en Chrome, sigue estos pasos:&lt;/p&gt;

&lt;h2&gt;
  
  
  Requerimientos de Hardware
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;4GB de RAM&lt;/li&gt;
&lt;li&gt;GPU disponible&lt;/li&gt;
&lt;li&gt;Mínimo 22GB de espacio&lt;/li&gt;
&lt;li&gt;Windows 10.11 o macOS Ventura o versiones más recientes (sin especificación para Linux)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No hay soporte aún para:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ChromeOS&lt;/li&gt;
&lt;li&gt;Chrome iOS&lt;/li&gt;
&lt;li&gt;Chrome Android&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requerimientos de Software
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Instalar Chrome Canary&lt;/li&gt;
&lt;li&gt;Activar la AI y los modelos modificando los siguientes flags:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Ve a &lt;code&gt;chrome://flags/#optimization-guide-on-device-model&lt;/code&gt; y selecciona "Enabled BypassPerfRequirement"&lt;/li&gt;
&lt;li&gt;Ve a &lt;code&gt;chrome://flags/#prompt-api-for-gemini-nano&lt;/code&gt; y selecciona "Enable"&lt;/li&gt;
&lt;li&gt;Reinicia Chrome&lt;/li&gt;
&lt;li&gt;Verifica la instalación ejecutando este comando en la consola:
&lt;code&gt;(await ai.languageModel.capabilities()).available&lt;/code&gt;.
Debe devolver "readily".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si falla, prueba lo siguiente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ejecuta &lt;code&gt;await ai.languageModel.create()&lt;/code&gt; para intentar forzar a Chrome a activar el API (aunque podría no funcionar).&lt;/li&gt;
&lt;li&gt;Ve a &lt;code&gt;chrome://components&lt;/code&gt; y revisa si el componente "Optimization Guide On Device Model" tiene una versión igual o mayor a &lt;code&gt;2024.5.21.1031&lt;/code&gt;. Si no tiene versión, haz clic en "check for updates" e intenta nuevamente.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; A veces, la instalación del modelo puede tardar. Ten paciencia y repite el proceso si es necesario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Activación de Modelos
&lt;/h2&gt;

&lt;p&gt;Para activar los modelos, habilita los siguientes flags en Chrome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#prompt-api-for-gemini-nano&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#summarization-api-for-gemini-nano&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#rewriter-api-for-gemini-nano&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#writer-api-for-gemini-nano&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#language-detection-api&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Las funciones disponibles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prompt API
&lt;/h3&gt;

&lt;p&gt;Este es el modelo más sencillo, utilizado para tareas generales. Al enviarle un prompt, intenta devolver una respuesta. Aquí un ejemplo básico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Explain what JavaScript is&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;También puedes utilizar systemPrompt para pasarle instrucciones adicionales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are an expert in JavaScript, providing helpful code best practices.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Language Detection
&lt;/h3&gt;

&lt;p&gt;Esta API detecta el idioma de un texto, soportando más de 100 lenguajes y variantes. &lt;/p&gt;

&lt;p&gt;Ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDetector&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;detector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bonjour le monde&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectedLanguage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writer y Rewriter
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Writer API:&lt;/strong&gt; Crea contenido nuevo.&lt;br&gt;
Ejemplo: escribir un borrador de una solicitud al banco.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write a email asking for feedback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rewriter API:&lt;/strong&gt; Mejora o reestructura un texto ya existente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rewriter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rewriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rewriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rewrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;La inteligencia artificial es...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Use simple words.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Todos estos APIs están en fase experimental, por lo que es normal encontrar errores o inconsistencias. Todo feedback es bienvenido. Si te interesa estar al tanto de los cambios, puedes llenar este formulario para acceder a la documentación y recibir actualizaciones.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>web</category>
    </item>
    <item>
      <title>AI Local con el API de Chrome</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Mon, 30 Sep 2024 17:18:44 +0000</pubDate>
      <link>https://dev.to/marianocodes/ai-local-con-el-api-de-chrome-4mkp</link>
      <guid>https://dev.to/marianocodes/ai-local-con-el-api-de-chrome-4mkp</guid>
      <description>&lt;p&gt;Es común que la mayoría de los productos que utilizan inteligencia artificial (IA) lo hagan a través del consumo de un API, que a su vez se conecta a un servidor para retornar los resultados a la web. Esto tiene mucho sentido cuando las tareas son intensas y requieren un gran poder de procesamiento.&lt;/p&gt;

&lt;p&gt;Pero, ¿existe alguna opción más eficiente para tareas sencillas?&lt;/p&gt;

&lt;p&gt;El equipo de Chrome ha lanzado de manera experimental un API que permite interactuar con el modelo Gemini Nano de manera local. Así, se elimina la necesidad de utilizar modelos más grandes, como Gemini Pro 1.5, para tareas complejas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Principales Diferencias
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integración Local&lt;/strong&gt;: No es necesario hacer deploy del modelo. Al estar integrado directamente en el navegador, este se encarga de gestionar la descarga, actualización y mejoras. El desarrollador solo debe preocuparse por integrarlo en su aplicación.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eficiencia en la Descarga&lt;/strong&gt;: Al no requerir que la aplicación descargue el modelo, se mejora la eficiencia. Incluso los modelos pequeños, en el contexto web, pueden ser de gran tamaño. Por ejemplo, el modelo de transformer.js pesa alrededor de 60MB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mejora en el Rendimiento&lt;/strong&gt;: Esta integración local permite acceder a recursos del dispositivo, como el GPU, lo que mejora significativamente el rendimiento.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Beneficios de Correr un Modelo de Manera Local
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ahorro de Llamados al Servidor&lt;/strong&gt;: Al evitar consultas constantes al servidor, la aplicación web se vuelve más eficiente, reduciendo tiempos de espera.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacidad&lt;/strong&gt;: Los datos permanecen en el dispositivo, lo que añade una capa extra de seguridad al no tener que enviarlos a servidores externos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Uso Offline&lt;/strong&gt;: Una vez descargado, el modelo está disponible en el dispositivo, lo que permite su uso sin conexión a internet.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Arquitecturas Híbridas
&lt;/h3&gt;

&lt;p&gt;Aunque el modelo local es eficiente, no podemos descartar completamente el servidor. Este seguirá siendo necesario para procesar tareas más complejas. La clave está en encontrar el "sweet spot", es decir, el punto óptimo en el que se determine cuándo utilizar un modelo local y cuándo recurrir al servidor.&lt;/p&gt;

&lt;p&gt;Además, los modelos integrados pueden servir como respaldo en caso de fallos del servidor o falta de conexión a internet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitaciones
&lt;/h3&gt;

&lt;p&gt;Al ser un modelo pequeño y optimizado para correr en el navegador, tiene una capacidad más limitada. Por ahora, se recomienda su uso para tareas puntuales como traducciones, resúmenes o mejoras de texto. Este tipo de modelos son conocidos como "Expert Models", ya que son más eficientes para tareas específicas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Únete al Programa Experimental
&lt;/h3&gt;

&lt;p&gt;Si quieres probar este API, puedes unirte al programa experimental llenando el formulario en el en este &lt;a href="https://docs.google.com/forms/d/e/1FAIpQLSfZXeiwj9KO9jMctffHPym88ln12xNWCrVkMY_u06WfSTulQg/viewform" rel="noopener noreferrer"&gt;enlace&lt;/a&gt;. Recibirás acceso a la documentación y a un Google Group donde podrás mantenerte informado sobre las actualizaciones y cambios en el API.&lt;/p&gt;

&lt;p&gt;Aprende en el siguiente &lt;a href="https://dev.to/marianocodes/como-utilizar-el-api-experimental-de-ai-en-chrome-3c4m"&gt;post&lt;/a&gt; cómo empezar a utilizar este API y las funciones disponibles.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>web</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Performance: Los valores de Lighthouse y PageSpeed Insights son diferentes</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Tue, 18 Jun 2024 14:33:49 +0000</pubDate>
      <link>https://dev.to/marianocodes/por-que-los-valores-de-lighthouse-son-diferentes-a-los-de-pagespeed-insights-7f6</link>
      <guid>https://dev.to/marianocodes/por-que-los-valores-de-lighthouse-son-diferentes-a-los-de-pagespeed-insights-7f6</guid>
      <description>&lt;p&gt;Existen dos tipos de herramientas para hacer pruebas de performance en web:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Herramientas de laboratorio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Funcionan en condiciones específicas, por ejemplo:&lt;br&gt;
• No consideran el cache, bfCache, ni AMP&lt;br&gt;
• Ambientes controlados (geografía, dispositivo, velocidad)&lt;br&gt;
• Sirven en la etapa de desarrollo, antes de producción&lt;br&gt;
• Son estables, generalmente el score es el mismo aunque se corra la herramienta varias veces&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Herramientas de campo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Utilizan datos reales para comparar los resultados y determinar el score.&lt;/p&gt;

&lt;p&gt;Pueden ser datos recolectados manualmente o utilizando el Chrome UX Report&lt;/p&gt;

&lt;p&gt;🧪 Lighthouse en los Devtools de Chrome funciona como una herramienta de laboratorio. Lo puedes determinar porque al final del reporte vienen las condiciones en las que se corrieron las pruebas, por ejemplo:&lt;/p&gt;

&lt;p&gt;• Initial page load&lt;br&gt;
• Slow 4G throttling&lt;br&gt;
• Single page session&lt;br&gt;
• Using Chromium 125.0.0.0 with devtools&lt;br&gt;
• Emulated Moto G Power with Lighthouse 11.7.1&lt;/p&gt;

&lt;p&gt;⚡️ PageSpeed Insights por su parte entrega una información similar pero puedes detallar que las pruebas corren en diferentes condiciones:&lt;/p&gt;

&lt;p&gt;• Full visit durations&lt;br&gt;
• All Chrome versions&lt;br&gt;
• Various mobile devices&lt;br&gt;
• Various network connections&lt;br&gt;
• Latest 28-day collection period&lt;br&gt;
• Many samples (Chrome UX Report)&lt;/p&gt;

&lt;p&gt;Así que no te asustes, está bien que los valores sean diferentes, solo tienes que entender la razón.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Te ayudo a ser un mejor Web Developer&lt;br&gt;
Click + &lt;a href="https://www.linkedin.com/in/marianocodes/"&gt;Mariano Alvarez&lt;/a&gt; + Seguir + 🔔&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Like si te gustó este contenido ❤️&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>lighthouse</category>
      <category>performance</category>
      <category>corevitals</category>
    </item>
    <item>
      <title>Microsoft hace a un lado React</title>
      <dc:creator>Mariano Álvarez 🇨🇷</dc:creator>
      <pubDate>Tue, 11 Jun 2024 16:06:22 +0000</pubDate>
      <link>https://dev.to/marianocodes/microsoft-hace-a-un-lado-react-3hh6</link>
      <guid>https://dev.to/marianocodes/microsoft-hace-a-un-lado-react-3hh6</guid>
      <description>&lt;p&gt;Así como lo lees, recientemente Microsoft &lt;a href="https://blogs.windows.com/msedgedev/2024/05/28/an-even-faster-microsoft-edge/"&gt;anunció&lt;/a&gt; que logró una mejora de rendimiento del 76% en su navegador Microsoft Edge.&lt;/p&gt;

&lt;p&gt;En un experimento que realizaron al reemplazar un menú que fue construido originalmente con React con WebUI 2.0, su nueva librería de componentes.&lt;/p&gt;

&lt;p&gt;Impresionantemente, sus modificaciones tuvieron como resutlado que el componente sea un 42% más rápido, pero para aquellos que no tienen equipos más limitados, con menos de 8GB de RAM o sin estado sólido (SSD), se vio una mejora del 76%.&lt;/p&gt;

&lt;p&gt;Probablemente te estás preguntando:&lt;br&gt;
• ¿Un navegador con React?&lt;br&gt;
¡Claro! Al final, todo se traduce a HTML, CSS y JavaScript.&lt;/p&gt;

&lt;p&gt;• ¿Qué significa esto para React?&lt;br&gt;
NADA, esto no es ni positivo ni negativo.&lt;/p&gt;

&lt;p&gt;• ¿React es lento?&lt;br&gt;
No, pero para ti, ¿qué es velocidad?&lt;/p&gt;

&lt;p&gt;Todas estas mejoras están disponibles en la versión 122 de Microsoft Edge.&lt;/p&gt;

&lt;p&gt;Te leo en los comentarios.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>microsoft</category>
    </item>
  </channel>
</rss>
