<?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: Sumeet Naik</title>
    <description>The latest articles on DEV Community by Sumeet Naik (@sumeetweb).</description>
    <link>https://dev.to/sumeetweb</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F576259%2F9075cc84-e23a-4195-879a-1b7e93beb21c.png</url>
      <title>DEV Community: Sumeet Naik</title>
      <link>https://dev.to/sumeetweb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sumeetweb"/>
    <language>en</language>
    <item>
      <title>Serve Markdown to AI Agents from Hugo on Cloudflare Pages (Free Plan)</title>
      <dc:creator>Sumeet Naik</dc:creator>
      <pubDate>Sun, 09 Aug 2026 05:45:54 +0000</pubDate>
      <link>https://dev.to/sumeetweb/serve-markdown-to-ai-agents-from-hugo-on-cloudflare-pages-free-plan-15cl</link>
      <guid>https://dev.to/sumeetweb/serve-markdown-to-ai-agents-from-hugo-on-cloudflare-pages-free-plan-15cl</guid>
      <description>&lt;p&gt;AI agents, LLM crawlers, and CLI tools don't want your HTML. They want the &lt;em&gt;content&lt;/em&gt;, i.e. the headings, lists and links without the nav bars, the theme toggles, and the 40 KB of markup around it. The polite way to give it to them is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation" rel="noopener noreferrer"&gt;content negotiation&lt;/a&gt;: when a client sends &lt;code&gt;Accept: text/markdown&lt;/code&gt;, hand back Markdown; otherwise, serve HTML like normal.&lt;/p&gt;

&lt;p&gt;The catch: content negotiation branches on a &lt;strong&gt;request&lt;/strong&gt; header, and a static host can't do that. A &lt;code&gt;_headers&lt;/code&gt; file on Cloudflare Pages only sets &lt;em&gt;response&lt;/em&gt; headers by path — it can't look at what the client asked for. So for years the answer was "use a real server."&lt;/p&gt;

&lt;p&gt;Cloudflare offers a path here, too: a &lt;a href="https://developers.cloudflare.com/workers-ai/features/markdown-conversion/" rel="noopener noreferrer"&gt;&lt;code&gt;toMarkdown&lt;/code&gt; conversion&lt;/a&gt; utility on Workers AI that turns documents into Markdown on demand. It's a fine tool, but a different job — it converts &lt;em&gt;arbitrary files&lt;/em&gt; at request time (and image conversions can dip into Workers AI billing), and it means adding a Workers AI binding as a dependency. For serving your own site's pages, you don't need it.&lt;/p&gt;

&lt;p&gt;You don't need any of that. This post shows how to do it on Hugo + Cloudflare Pages, entirely on the &lt;strong&gt;free plan&lt;/strong&gt;, with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hugo emitting a &lt;code&gt;.md&lt;/code&gt; twin of every page at build time, and&lt;/li&gt;
&lt;li&gt;One small Cloudflare Pages Function that swaps HTML for Markdown when — and only when — a client asks for it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The end result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-sD-&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Accept: text/markdown'&lt;/span&gt; https://sumeetnaik.com/blog/
&lt;span class="go"&gt;HTTP/2 200
&lt;/span&gt;&lt;span class="gp"&gt;content-type: text/markdown;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
&lt;span class="go"&gt;vary: accept
x-markdown-tokens: 32

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Blog
&lt;span class="go"&gt;
- [Serve Markdown to AI Agents ...](/blog/markdown-for-ai-agents-hugo-cloudflare-pages/) — 2026-07-14
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A browser hitting the same URL gets HTML, untouched.&lt;/p&gt;




&lt;h2&gt;
  
  
  The shape of the solution
&lt;/h2&gt;

&lt;p&gt;Two pieces, and they're independent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build time (Hugo):&lt;/strong&gt; generate &lt;code&gt;index.md&lt;/code&gt; next to every &lt;code&gt;index.html&lt;/code&gt;. Static files, cached at the edge like anything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request time (Pages Function):&lt;/strong&gt; a tiny middleware that reads &lt;code&gt;Accept&lt;/code&gt;, and if the client wants Markdown, serves the pre-built &lt;code&gt;.md&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the Markdown is pre-rendered, the function does almost no work — it just picks which file to return. That keeps it fast and well within the free tier's limits.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Make Hugo emit Markdown for every page
&lt;/h2&gt;

&lt;p&gt;Hugo's &lt;a href="https://gohugo.io/templates/output-formats/" rel="noopener noreferrer"&gt;output formats&lt;/a&gt; let one page render to multiple files. Define a &lt;code&gt;MARKDOWN&lt;/code&gt; format and turn it on for every page kind in your site config (&lt;code&gt;hugo.toml&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[outputs]&lt;/span&gt;
  &lt;span class="py"&gt;home&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"HTML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"RSS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MARKDOWN"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="py"&gt;section&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"HTML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MARKDOWN"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="py"&gt;page&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"HTML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MARKDOWN"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[outputFormats.MARKDOWN]&lt;/span&gt;
  &lt;span class="py"&gt;mediaType&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"text/markdown"&lt;/span&gt;  &lt;span class="c"&gt;# -&amp;gt; ".md" suffix&lt;/span&gt;
  &lt;span class="py"&gt;baseName&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"index"&lt;/span&gt;          &lt;span class="c"&gt;# -&amp;gt; &amp;lt;path&amp;gt;/index.md, beside index.html&lt;/span&gt;
  &lt;span class="py"&gt;isPlainText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;             &lt;span class="c"&gt;# don't run it through the HTML escaper&lt;/span&gt;
  &lt;span class="py"&gt;notAlternative&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;          &lt;span class="c"&gt;# keep it out of &amp;lt;link rel=alternate&amp;gt; discovery&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;baseName = "index"&lt;/code&gt; is the important bit: it drops &lt;code&gt;index.md&lt;/code&gt; right next to each &lt;code&gt;index.html&lt;/code&gt;, so &lt;code&gt;/blog/my-post/&lt;/code&gt; gets a sibling &lt;code&gt;/blog/my-post/index.md&lt;/code&gt;. Easy to map to later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Write the Markdown templates
&lt;/h2&gt;

&lt;p&gt;Output formats need templates. Hugo picks a template by &lt;em&gt;name + format&lt;/em&gt;, so a template ending in &lt;code&gt;.markdown.md&lt;/code&gt; only ever renders the &lt;code&gt;MARKDOWN&lt;/code&gt; format and won't collide with your HTML layouts. You need three generic ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A single page&lt;/strong&gt; — &lt;code&gt;layouts/_default/single.markdown.md&lt;/code&gt;. &lt;code&gt;.RawContent&lt;/code&gt; is the original Markdown source of the page, minus front matter — exactly what an agent wants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# {{ .Title }}
{{ with .Date }}{{ if not .IsZero }}
_{{ .Format "January 2, 2006" }}_
{{ end }}{{ end }}
{{ .RawContent }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A section list&lt;/strong&gt; — &lt;code&gt;layouts/_default/list.markdown.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# {{ .Title }}
{{ with .RawContent }}
{{ . }}{{ end }}
{{ range .Pages }}- [{{ .Title }}]({{ .RelPermalink }}){{ with .Date }}{{ if not .IsZero }} — {{ .Format "2006-01-02" }}{{ end }}{{ end }}
{{ end }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The home page&lt;/strong&gt; — &lt;code&gt;layouts/index.markdown.md&lt;/code&gt;. Home pages are usually bespoke, so build it from your site params / menus rather than &lt;code&gt;.RawContent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# {{ .Site.Params.name }}

{{ .Site.Params.intro }}

## Pages
{{ range .Site.Menus.main }}
- [{{ .Name }}]({{ .URL }}){{ end }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;hugo&lt;/code&gt; and you'll see &lt;code&gt;index.md&lt;/code&gt; files appear throughout &lt;code&gt;public/&lt;/code&gt;. That's the generic case done.&lt;/p&gt;

&lt;h3&gt;
  
  
  The gotcha: pages that live in front matter
&lt;/h3&gt;

&lt;p&gt;Here's the trap almost every Hugo site hits. Many "pages" store their content in &lt;strong&gt;front matter&lt;/strong&gt;, not in the Markdown body — a &lt;code&gt;/uses&lt;/code&gt; page with a &lt;code&gt;groups:&lt;/code&gt; list, a &lt;code&gt;/resume&lt;/code&gt; with &lt;code&gt;jobs:&lt;/code&gt;, a &lt;code&gt;/now&lt;/code&gt; with &lt;code&gt;sections:&lt;/code&gt;. For those, &lt;code&gt;.RawContent&lt;/code&gt; is &lt;em&gt;empty&lt;/em&gt;, and the generic template above gives you a lonely &lt;code&gt;# Title&lt;/code&gt; with nothing under it.&lt;/p&gt;

&lt;p&gt;The fix is one Markdown template per custom layout, mirroring what the HTML template does — just emitting Markdown instead of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s. If your &lt;code&gt;resume&lt;/code&gt; page renders from a &lt;code&gt;jobs&lt;/code&gt; array in the HTML layout, add &lt;code&gt;layouts/_default/resume.markdown.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# {{ .Title }}

{{ .Params.sub }}

## Experience
{{ range .Params.jobs }}
### {{ .title }}

_{{ .dateRange }}_ — {{ .meta }}

{{ range .points }}- {{ . }}
{{ end }}{{ end }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same idea for a &lt;code&gt;uses&lt;/code&gt; page (&lt;code&gt;delimit&lt;/code&gt;, nested &lt;code&gt;range&lt;/code&gt;) or a &lt;code&gt;now&lt;/code&gt; page. Whatever structured data your HTML layout reads, your Markdown layout reads the same fields. Hugo resolves &lt;code&gt;&amp;lt;layout&amp;gt;.markdown.md&lt;/code&gt; automatically for any page whose front matter sets &lt;code&gt;layout: "resume"&lt;/code&gt; (etc.).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; for every custom HTML layout you have, check whether the content is in the body or the front matter. Body content → the generic &lt;code&gt;single.markdown.md&lt;/code&gt; covers you. Front-matter content → write a matching &lt;code&gt;*.markdown.md&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3 — The Cloudflare Pages Function
&lt;/h2&gt;

&lt;p&gt;Now the negotiation. Drop a single file at &lt;code&gt;functions/_middleware.js&lt;/code&gt; in your repo root — Cloudflare Pages &lt;a href="https://developers.cloudflare.com/pages/functions/" rel="noopener noreferrer"&gt;auto-detects the &lt;code&gt;functions/&lt;/code&gt; directory&lt;/a&gt;, no config, no &lt;code&gt;wrangler.toml&lt;/code&gt;, no dependencies. &lt;code&gt;_middleware.js&lt;/code&gt; runs on &lt;strong&gt;every&lt;/strong&gt; request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&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;accepts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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="c1"&gt;// Require markdown to be explicitly acceptable — a bare `*/*` keeps HTML.&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/text&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;markdown/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accepts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&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;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&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;mdPath&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;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;mdPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.md&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;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mdPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/index.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// extensionless page URL&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                      &lt;span class="c1"&gt;// real file (css, rss.xml, ...) — leave it&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;mdUrl&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;URL&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="nx"&gt;mdUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mdPath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Fetch the static .md that Hugo built for this page.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mdUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c1"&gt;// If there's no .md for this path, fall back to HTML. A missing asset can be a&lt;/span&gt;
  &lt;span class="c1"&gt;// 404 (production) OR the root index.html served with status 200 (wrangler&lt;/span&gt;
  &lt;span class="c1"&gt;// dev), so reject anything that isn't actually markdown — don't trust status.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&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;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="o"&gt;||&lt;/span&gt; &lt;span class="dl"&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="o"&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;ok&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="sr"&gt;/html/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&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;Headers&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;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;text/markdown; charset=utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// let the runtime recompute&lt;/span&gt;
  &lt;span class="nx"&gt;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-markdown-tokens&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&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="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&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="nc"&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&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;A few things worth calling out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;context.next(request)&lt;/code&gt;&lt;/strong&gt; is the key trick. Called with no argument it continues the pipeline for the &lt;em&gt;current&lt;/em&gt; URL; called with a &lt;code&gt;Request&lt;/code&gt; for a &lt;em&gt;different&lt;/em&gt; URL, it fetches that static asset instead. That's how we grab &lt;code&gt;index.md&lt;/code&gt; without a second network hop. It does &lt;strong&gt;not&lt;/strong&gt; re-enter the middleware, so there's no infinite loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We require &lt;code&gt;text/markdown&lt;/code&gt; literally.&lt;/strong&gt; A browser sends &lt;code&gt;Accept: text/html,...,*/*;q=0.8&lt;/code&gt;, which doesn't match, so browsers are never affected. Agents opt in explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;x-markdown-tokens&lt;/code&gt;&lt;/strong&gt; is a courtesy header. There's no tokenizer at the edge, so &lt;code&gt;chars / 4&lt;/code&gt; is the standard rough estimate for English — good enough for a client to decide whether to fetch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The bug you'll hit if you skip the content-type check
&lt;/h3&gt;

&lt;p&gt;When the &lt;code&gt;.md&lt;/code&gt; doesn't exist, you'd expect a clean 404. But &lt;code&gt;wrangler pages dev&lt;/code&gt; serves the root &lt;code&gt;index.html&lt;/code&gt; with &lt;strong&gt;status 200&lt;/strong&gt; as a fallback — so a naïve &lt;code&gt;if (!res.ok)&lt;/code&gt; check &lt;em&gt;passes&lt;/em&gt;, and you happily wrap HTML in a &lt;code&gt;text/markdown&lt;/code&gt; content-type. I hit exactly this while testing a bogus URL. Checking that the fetched asset's content-type isn't HTML fixes it for both dev and production. Don't trust the status code alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Tell caches the response varies
&lt;/h2&gt;

&lt;p&gt;One header keeps CDNs honest. Add &lt;code&gt;Vary: Accept&lt;/code&gt; so a cache never serves the Markdown variant to a browser (or vice-versa). The function already sets it on Markdown responses; add it site-wide for the HTML side via &lt;code&gt;static/_headers&lt;/code&gt;:&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;/*
  Vary: Accept
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test it locally
&lt;/h2&gt;

&lt;p&gt;Build, then run Cloudflare's own dev server against the output with the function wired in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo
npx wrangler pages dev public &lt;span class="nt"&gt;--port&lt;/span&gt; 8788
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then exercise both paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Markdown for agents&lt;/span&gt;
curl &lt;span class="nt"&gt;-sD-&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Accept: text/markdown'&lt;/span&gt; http://localhost:8788/blog/ | &lt;span class="nb"&gt;head&lt;/span&gt;

&lt;span class="c"&gt;# HTML for everyone else — unchanged&lt;/span&gt;
curl &lt;span class="nt"&gt;-sD-&lt;/span&gt; http://localhost:8788/blog/ | &lt;span class="nb"&gt;head&lt;/span&gt;

&lt;span class="c"&gt;# Real files are left alone&lt;/span&gt;
curl &lt;span class="nt"&gt;-sD-&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Accept: text/markdown'&lt;/span&gt; http://localhost:8788/rss.xml | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check: &lt;code&gt;Content-Type: text/markdown&lt;/code&gt;, an &lt;code&gt;x-markdown-tokens&lt;/code&gt; header, and a Markdown body on the first; plain HTML on the second; and your feed untouched on the third. A non-existent URL should fall back to HTML, not a Markdown-labelled 404.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this stays free
&lt;/h2&gt;

&lt;p&gt;Nothing here touches a paid feature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hugo's extra output is just more static files.&lt;/li&gt;
&lt;li&gt;Pages Functions are included on the &lt;strong&gt;free plan&lt;/strong&gt; (a generous daily request allowance), and this one does trivial work — read a header, fetch a pre-built file, set three headers.&lt;/li&gt;
&lt;li&gt;No KV, no D1, no Workers paid tier, no build plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Three moving parts — an output format, a handful of &lt;code&gt;*.markdown.md&lt;/code&gt; templates, and ~30 lines of middleware — and every page on your site now speaks Markdown to anything that asks for it, while humans keep getting the full experience. It pairs nicely with an &lt;a href="///agents.md"&gt;&lt;code&gt;agents.md&lt;/code&gt;&lt;/a&gt; and &lt;code&gt;Link: rel="describedby"&lt;/code&gt; headers if you want to go further in making your site legible to machines.&lt;/p&gt;

&lt;p&gt;The two things I'd remember if you're adapting this to your own theme: &lt;strong&gt;write a Markdown template for every front-matter-driven layout&lt;/strong&gt;, and &lt;strong&gt;guard on the fetched content-type&lt;/strong&gt;, not the status code. Everything else is mechanical.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>agents</category>
    </item>
    <item>
      <title>I Built an AI Pipeline That Writes My Tech Posts While I Code</title>
      <dc:creator>Sumeet Naik</dc:creator>
      <pubDate>Fri, 29 May 2026 07:53:58 +0000</pubDate>
      <link>https://dev.to/sumeetweb/i-built-an-ai-pipeline-that-writes-my-tech-posts-while-i-code-2e3b</link>
      <guid>https://dev.to/sumeetweb/i-built-an-ai-pipeline-that-writes-my-tech-posts-while-i-code-2e3b</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hermes Content Pipeline&lt;/strong&gt;: a daily dev-to-social content workflow that turns your actual development work into LinkedIn, Twitter, and blog drafts, delivered to Telegram for review.&lt;/p&gt;

&lt;p&gt;The problem: as a developer with a full-time job, I was doing meaningful work every day but never publishing about it. The friction wasn't the writing, it was the capture. By the time I sat down to draft a post, I'd forgotten the trade-offs I'd weighed, the bugs I'd worked around, and the decisions that shaped the work.&lt;/p&gt;

&lt;p&gt;The pipeline solves this in three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auto-capture during work&lt;/strong&gt;: A Hermes skill (&lt;code&gt;content-capture&lt;/code&gt;) automatically logs trade-off decisions, bugs (as principles, not raw debugging), and task completions to a sidecar notes file as you work. No interruption, no extra input needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;"Draft posts for today's tasks"&lt;/strong&gt;: A Python script parses the session database and sidecar notes into a structured prompt, then a skill calls the LLM to generate three drafts: a ~300 word LinkedIn post, a Twitter thread (or single tweet, contextually chosen), and a substantive blog post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review and refine from mobile&lt;/strong&gt;: All three drafts land on Telegram. Reply with "make linkedin shorter" or "focus on the auth bug in twitter" and the refinement skill re-generates only the affected draft.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key insight: the best dev content comes from real work, not from staring at a blank page. This pipeline eliminates the blank page by deriving posts from what you already did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&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%2Fk87ia4ky2ietvhy9qisq.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%2Fk87ia4ky2ietvhy9qisq.png" alt="Post drafts sent via telegram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the pipeline in action, a real session where I built the project itself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;: Sidecar notes from a work session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// trade: Sidecar file over DB column — keeps notes portable — needs manual cleanup
// trade: Minimal approach over fully integrated — simpler, no cron complexity
// bug: Telegram Markdown parsing rejects some characters — built fallback to plain text
// note: Built content-capture skill that auto-logs trade-offs during sessions
// note: Built draft_posts.py pipeline — reads state.db + notes, calls LLM, generates drafts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt; (LinkedIn draft, 140 words after refinement):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every developer has a graveyard of half-formed ideas — debugging discoveries, architecture trade-offs, patterns that took hours to find.&lt;/p&gt;

&lt;p&gt;I've been building content capture into our tooling, and the biggest lesson: capture is everything.&lt;/p&gt;

&lt;p&gt;We use a simple sidecar file for logging insights. Manual cleanup required. No cron jobs. No automation.&lt;/p&gt;

&lt;p&gt;And that simplicity is the feature.&lt;/p&gt;

&lt;p&gt;The minimal approach — agent-driven markers, a flat file, simple pipeline — has been more useful than any elaborate setup I've attempted.&lt;/p&gt;

&lt;p&gt;Do you have systems to capture lessons from your dev sessions, or does most of it stay trapped in your head?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Refinement in action&lt;/strong&gt;: Started at ~300 words, refined down to 140 with &lt;code&gt;"make linkedin more concise, under 150 words"&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/sumeetweb/hermes-content-pipeline" rel="noopener noreferrer"&gt;github.com/sumeetweb/hermes-content-pipeline&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── skills/
│   ├── content-capture/SKILL.md       # Auto-logging during sessions
│   ├── content-generator/SKILL.md     # LLM-driven draft generation
│   └── refine-posts/SKILL.md          # Natural language refinement
├── scripts/
│   ├── parse_drafts.py                # Session parsing + prompt building
│   └── telebot_send.py                # Telegram delivery
├── tests/                             # Unit tests
└── run.sh                             # Venv-aware runner

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  My Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hermes Agent&lt;/strong&gt;: session logging (SQLite &lt;code&gt;state.db&lt;/code&gt;), skill system, Telegram gateway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python stdlib&lt;/strong&gt;: SQLite3, json, pathlib, urllib (Telegram Bot API)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI SDK&lt;/strong&gt;: LLM calls using whatever model Hermes is configured with (minimax-m2.7 via Ollama Cloud in my setup)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hermes Skill Framework&lt;/strong&gt;: YAML frontmatter + markdown for the auto-logging skill&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Used Hermes Agent
&lt;/h2&gt;

&lt;p&gt;This project leans on four Hermes Agent capabilities that no other agent framework provides as cleanly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Session Database (state.db)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hermes stores every conversation in a local SQLite database with full message history. The &lt;code&gt;parse_drafts.py&lt;/code&gt; script queries this directly, no API, no export step, no manual copy-paste. The session transcript IS the source of truth. This is what makes "derive posts from real work" possible without any extra effort from the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Skill System for Zero-Friction Capture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;content-capture&lt;/code&gt; skill uses Hermes' skill framework (YAML frontmatter + markdown body) to instruct the agent to automatically log &lt;code&gt;// trade:&lt;/code&gt;, &lt;code&gt;// bug:&lt;/code&gt;, &lt;code&gt;// note:&lt;/code&gt; markers to a sidecar file. The agent recognizes significant moments during the session and logs them silently. The user never has to stop what they're doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Model-Agnostic LLM Config&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The skill layer reads model and provider settings directly from &lt;code&gt;~/.hermes/config.yaml&lt;/code&gt; and &lt;code&gt;~/.hermes/.env&lt;/code&gt;. It inherits whatever model the user is already running. I'm using &lt;code&gt;minimax-m2.7&lt;/code&gt; via Ollama Cloud, but someone else could be on Claude, GPT-4o, or DeepSeek and it would just work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Telegram Gateway Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hermes' Telegram gateway config and channel directory (&lt;code&gt;channel_directory.json&lt;/code&gt;) means the pipeline can discover the bot token and home chat ID automatically. No environment variable setup, no manual configuration. The drafts arrives to the user directly on their phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Hermes was the right fit&lt;/strong&gt;: Other agent frameworks would require building the session logging, skill system, and messaging integration from scratch. Hermes provides all three out of the box, and the content pipeline simply connects them. The project is ~600 lines of Python + 2 skills connecting existing Hermes infrastructure, not ~3000 lines reinventing it.&lt;/p&gt;

</description>
      <category>hermesagentchallenge</category>
      <category>devchallenge</category>
      <category>agents</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Team Dashboard - Manage Your Team Efficiently with KendoReact</title>
      <dc:creator>Sumeet Naik</dc:creator>
      <pubDate>Sun, 28 Sep 2025 20:11:27 +0000</pubDate>
      <link>https://dev.to/sumeetweb/team-dashboard-manage-your-team-efficiently-with-kendoreact-5bdc</link>
      <guid>https://dev.to/sumeetweb/team-dashboard-manage-your-team-efficiently-with-kendoreact-5bdc</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kendoreact-2025-09-10"&gt;KendoReact Free Components Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Team Dashboard&lt;/strong&gt; is an enterprise-grade team performance dashboard that revolutionizes how managers track productivity, attendance, and KPIs across distributed teams. Built with React and powered by KendoReact's professional UI components, this application transforms complex performance data into actionable insights.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 &lt;strong&gt;Problems Solved:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Data Accessibility Challenge&lt;/strong&gt;: Traditional performance management often involves scattered spreadsheets, complex reports, and time-consuming data gathering. Team Dashboard centralizes all employee metrics in an intuitive, searchable interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Historical Analysis Bottleneck&lt;/strong&gt;: Managers typically struggle to access past performance data quickly. Our AI-powered natural language query system allows instant access to historical insights with simple conversational queries like &lt;em&gt;"Show me Q2 results for developers in Bangalore"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Team Management Complexity&lt;/strong&gt;: Managing large teams requires efficient CRUD operations for employee data. The dashboard provides professional-grade employee management with inline editing, bulk operations, and smart filtering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual Analytics Gap&lt;/strong&gt;: Raw performance data is difficult to interpret. TeamPulse Pro includes interactive charts and trend analysis that transform numbers into visual stories managers can act upon.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏢 &lt;strong&gt;Target Users:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engineering Managers&lt;/strong&gt; tracking developer productivity and code quality metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HR Directors&lt;/strong&gt; monitoring employee satisfaction and retention indicators
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Department Heads&lt;/strong&gt; analyzing team performance across multiple locations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C-Suite Executives&lt;/strong&gt; requiring high-level performance overviews with drill-down capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎥 &lt;strong&gt;Video Walkthrough&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/P58waPqEp20"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 &lt;strong&gt;Live Application&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Demo URL&lt;/strong&gt;: &lt;a href="https://nishikantaray.github.io/KendoReact-Free-Components-Challenge/" rel="noopener noreferrer"&gt;Click Here&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/NishikantaRay/KendoReact-Free-Components-Challenge" rel="noopener noreferrer"&gt;https://github.com/NishikantaRay/KendoReact-Free-Components-Challenge&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📸 &lt;strong&gt;Screenshots &amp;amp; Features&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Main Dashboard Interface
&lt;/h4&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%2F07m55nmk0e19matra3cl.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%2F07m55nmk0e19matra3cl.png" alt="Main Dashboard" width="800" height="382"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Employee data grid with real-time search, sorting, and filtering capabilities&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  AI-Powered Query Assistant
&lt;/h4&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%2Fngs6fqahbubw6uamx1dr.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%2Fngs6fqahbubw6uamx1dr.png" alt="Query Assistant" width="312" height="296"&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%2Fhq1fogx6tce3wk7nnbn5.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%2Fhq1fogx6tce3wk7nnbn5.png" alt="Query Assistant" width="272" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Natural language query interface powered by Nuclia RAG technology&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Performance Analytics
&lt;/h4&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%2Fkzpx7d23xcu6dhtoqoxi.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%2Fkzpx7d23xcu6dhtoqoxi.png" alt="Analytics" width="800" height="380"&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%2F6q7g2p6yjzmdd3cchv5o.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%2F6q7g2p6yjzmdd3cchv5o.png" alt="Analytics" width="800" height="380"&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%2Fl2g1hppeledb6yw6c5kr.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%2Fl2g1hppeledb6yw6c5kr.png" alt="Analytics" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Interactive performance trends and KPI visualizations&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Employee Management Dialog
&lt;/h4&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%2Fho6qq24akiqiu4qt7dvs.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%2Fho6qq24akiqiu4qt7dvs.png" alt="Employee Grid" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Professional forms for employee data management with validation&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  KendoReact Components Used
&lt;/h2&gt;

&lt;p&gt;We successfully integrated 12 free KendoReact components throughout the application:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core Data Components&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grid - The centerpiece employee data grid with sorting, filtering, and pagination&lt;/li&gt;
&lt;li&gt;GridColumn - Individual column definitions with custom rendering&lt;/li&gt;
&lt;li&gt;GridToolbar - Action toolbar for grid operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;User Interface Components&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Button - Used extensively across all dialogs and action bars&lt;/li&gt;
&lt;li&gt;Input - Text input fields for search and form data entry&lt;/li&gt;
&lt;li&gt;Switch - Toggle controls for dark mode and compact view settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Form &amp;amp; Selection Components&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DatePicker - Date selection for employee hire dates&lt;/li&gt;
&lt;li&gt;DropDownList - Team and role selection dropdowns with filtering capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Modal &amp;amp; Navigation Components&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dialog - Modal windows for employee add/edit operations&lt;/li&gt;
&lt;li&gt;DialogActionsBar - Standardized button containers for dialog actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Feedback Components&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Notification - Individual notification messages for user feedback&lt;/li&gt;
&lt;li&gt;NotificationGroup - Container managing multiple notification instances&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AI Coding Assistant Usage
&lt;/h2&gt;

&lt;p&gt;We extensively leveraged AI coding assistance throughout the development process, which significantly accelerated my workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Architecture &amp;amp; Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rapid Scaffolding: Used AI to generate initial component structures and establish proper KendoReact imports&lt;/li&gt;
&lt;li&gt;Best Practices Implementation: AI helped ensure proper component organization and React hooks usage&lt;/li&gt;
&lt;li&gt;Configuration Assistance: Streamlined the setup of complex grid configurations and chart integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code Generation &amp;amp; Optimization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CRUD Operations: AI accelerated the development of create, read, update, and delete functionality for employee management&lt;/li&gt;
&lt;li&gt;State Management: Helped implement efficient React state patterns for handling employee data and UI states&lt;/li&gt;
&lt;li&gt;Event Handling: Generated robust event handlers for grid operations, form submissions, and user interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Styling &amp;amp; Responsiveness
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CSS Integration: AI assisted in properly integrating KendoReact themes with custom styling&lt;/li&gt;
&lt;li&gt;Responsive Design: Helped create layouts that work seamlessly across desktop and mobile devices&lt;/li&gt;
&lt;li&gt;Theme Consistency: Ensured consistent styling patterns throughout all components&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Debugging &amp;amp; Problem Solving
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Error Resolution: AI provided solutions for component integration challenges&lt;/li&gt;
&lt;li&gt;Performance Optimization: Suggested improvements for grid rendering and data handling&lt;/li&gt;
&lt;li&gt;Cross-browser Compatibility: Helped identify and resolve potential browser-specific issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Natural Language Query System:
&lt;/h2&gt;

&lt;p&gt;We integrated Nuclia's AI capabilities directly into the dashboard sidebar, creating an intelligent assistant that allows managers to query historical performance data using conversational language.&lt;/p&gt;

&lt;p&gt;Some Sample Queries Supported:&lt;/p&gt;

&lt;p&gt;"Show me Q2 results for developers in Bangalore"&lt;br&gt;
"What was the average performance score last month?"&lt;br&gt;
"Which team has the highest productivity?"&lt;br&gt;
"How many employees joined in 2024?"&lt;/p&gt;

&lt;p&gt;Thank you for considering our submission for the KendoReact Free Components Challenge!&lt;/p&gt;

&lt;h2&gt;
  
  
  Team Members:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a class="mentioned-user" href="https://dev.to/nishikantaray"&gt;@nishikantaray&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a class="mentioned-user" href="https://dev.to/sumeetweb"&gt;@sumeetweb&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>kendoreactchallenge</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>TravelMate AI: Real-Time AI Travel Planner Powered by Redis Stack</title>
      <dc:creator>Sumeet Naik</dc:creator>
      <pubDate>Sat, 09 Aug 2025 21:47:59 +0000</pubDate>
      <link>https://dev.to/sumeetweb/travelmate-ai-real-time-ai-travel-planner-powered-by-redis-stack-4cn2</link>
      <guid>https://dev.to/sumeetweb/travelmate-ai-real-time-ai-travel-planner-powered-by-redis-stack-4cn2</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/redis-2025-07-23"&gt;Redis AI Challenge&lt;/a&gt;: Real-Time AI Innovators&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;TravelMate AI is an intelligent travel planning application that demonstrates Redis capabilities beyond traditional caching. The application leverages semantic caching, vector search, and real-time features to provide instant, personalized travel recommendations and itinerary planning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/sumeetweb/TravelMate-Redis-AI" rel="noopener noreferrer"&gt;https://github.com/sumeetweb/TravelMate-Redis-AI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Demo Video&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=cReF-pLsH-g" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=cReF-pLsH-g&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Homepage&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjv6x8wfqs55j2oqo7to3.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%2Fjv6x8wfqs55j2oqo7to3.png" alt="TravelMate AI Home" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without Redis Cache&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsi934oitxzkncwwi3b8.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%2Fgsi934oitxzkncwwi3b8.png" alt="Without Redis Cache" width="800" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With Redis Cache&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8ynvs2fm1u9fcpn3q8o.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%2Fz8ynvs2fm1u9fcpn3q8o.png" alt="With Redis Cache" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day-wise Itinerary Info&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetdwp6x5ufllmw5x8tui.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%2Fetdwp6x5ufllmw5x8tui.png" alt="Day-wise Itinerary Info" width="800" height="806"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Itinerary Info Map View&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw57wtfpaktacpzdt4i5h.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%2Fw57wtfpaktacpzdt4i5h.png" alt="Itinerary Info Map View" width="799" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Redis 8
&lt;/h2&gt;

&lt;p&gt;TravelMate AI showcases Redis as a comprehensive AI application platform through multiple advanced features:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Vector Search &amp;amp; Semantic Caching
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector Database&lt;/strong&gt;: Storing query embeddings for semantic similarity matching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HNSW Algorithm&lt;/strong&gt;: Efficient nearest-neighbor search for query similarity detection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intelligent Caching&lt;/strong&gt;: 95% similarity threshold for cache hits, eliminating redundant AI calls&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Redis JSON for Complex Data Storage
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured Itineraries&lt;/strong&gt;: Complete travel plans stored as JSON documents without serialization overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Location Metadata&lt;/strong&gt;: Detailed place information with coordinates, descriptions, and timing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Preferences&lt;/strong&gt;: Session context and personalization settings as JSON objects&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Real-time Communication with Pub/Sub
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Updates&lt;/strong&gt;: Server-Sent Events pipeline powered by Redis Pub/Sub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progress Indicators&lt;/strong&gt;: Real-time streaming of AI generation progress to frontend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-client Support&lt;/strong&gt;: Concurrent user sessions with isolated event streams&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Event Logging with Redis Streams
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complete Audit Trail&lt;/strong&gt;: Every user interaction logged with timestamps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Sourcing&lt;/strong&gt;: Replay capabilities for debugging and optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Flow&lt;/strong&gt;: Complete interaction sequences for user experience analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. Performance Monitoring with TimeSeries
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time Metrics&lt;/strong&gt;: Response times, cache hit rates, and performance trends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Dashboard&lt;/strong&gt;: Streaming performance data to frontend analytics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historical Analysis&lt;/strong&gt;: Time-based performance tracking with granular data points&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Technical Architecture
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend (React/Next.js) 
    ↕ WebSocket/SSE
Backend API (Node.js/Express)
    ↕ 
Redis Stack (Vector Search, JSON, Pub/Sub, Streams, Embeddings Cache)
    ↕
OpenAI API (GPT-4, Embeddings)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TravelMate AI proves Redis isn't just fast storage - it's an intelligent platform for modern AI applications. From semantic understanding to real-time experiences, Redis powers every aspect of intelligent travel planning. The question isn't whether Redis can handle AI workloads, but how much more can you build when you stop thinking of Redis as just a cache?&lt;/p&gt;

&lt;h2&gt;
  
  
  Team Submission: Team JCoders
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/sumeetweb"&gt;Sumeet Naik&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/nishikantaray"&gt;Nishikanta Ray&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redischallenge</category>
      <category>devchallenge</category>
      <category>database</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
