{"componentChunkName":"component---src-templates-blog-post-js","path":"/local-llm-blog-digest/","result":{"data":{"site":{"siteMetadata":{"title":"Aparna's Personal Space","author":"Aparna Ravindra"}},"markdownRemark":{"id":"45630e12-35a6-5a43-945c-45a89adb53b4","excerpt":"I built a small system to generate a daily blog digest using a local LLM and Notion. Here’s the technical walkthrough—kept simple and practical. 1. RSS…","html":"<p>I built a small system to generate a <strong>daily blog digest using a local LLM and Notion</strong>. Here’s the technical walkthrough—kept simple and practical.</p>\n<hr>\n<h3>1. RSS ingestion</h3>\n<p>Defined a <code class=\"language-text\">feeds.yaml</code> with sources and limits.</p>\n<p>Used <code class=\"language-text\">feedparser</code> to pull entries and filtered:</p>\n<ul>\n<li>only recent posts</li>\n<li>limited per day</li>\n</ul>\n<hr>\n<h3>2. Content extraction</h3>\n<p>For each post:</p>\n<ul>\n<li>fetched the article URL</li>\n<li>extracted clean text using <code class=\"language-text\">newspaper3k</code></li>\n</ul>\n<p>Key constraint:</p>\n<ul>\n<li>trimmed content (~1500 chars) to keep LLM input bounded</li>\n</ul>\n<hr>\n<h3>3. Local LLM summarization</h3>\n<p>Instead of external APIs, used Ollama:</p>\n<ul>\n<li>model: <code class=\"language-text\">gemma4:e4b</code></li>\n<li>invoked via HTTP (<code class=\"language-text\">/api/generate</code>)</li>\n<li>prompt enforced structured JSON output</li>\n</ul>\n<p>Handled common issues:</p>\n<ul>\n<li>markdown-wrapped JSON</li>\n<li>inconsistent formatting → cleaned before parsing</li>\n</ul>\n<hr>\n<h3>🔧 Prerequisite: Running the model</h3>\n<p>Before summarization works, you need a local model running via Ollama.</p>\n<div class=\"gatsby-highlight\" data-language=\"bashid=\"lg3c5j\"\"><pre class=\"language-bashid=\"lg3c5j\"\"><code class=\"language-bashid=\"lg3c5j\"\">ollama run gemma4:e4b</code></pre></div>\n<p>This starts a local server at:</p>\n<div class=\"gatsby-highlight\" data-language=\"textid=\"twh0nt\"\"><pre class=\"language-textid=\"twh0nt\"\"><code class=\"language-textid=\"twh0nt\"\">http://localhost:11434</code></pre></div>\n<p>The script sends prompts to <code class=\"language-text\">/api/generate</code> and receives summaries.</p>\n<hr>\n<h3>4. Notion integration</h3>\n<p>Used Notion API via <code class=\"language-text\">notion-client</code> to:</p>\n<ul>\n<li>create one page per day</li>\n<li>structure content with headings + paragraphs</li>\n<li>include clickable links</li>\n</ul>\n<p>Important fix:</p>\n<ul>\n<li>switched to timezone-aware datetime (<code class=\"language-text\">datetime.now().astimezone()</code>)\nto avoid incorrect dates</li>\n</ul>\n<hr>\n<h3>5. Configuration + secrets</h3>\n<ul>\n<li><code class=\"language-text\">.env</code> for API keys (<code class=\"language-text\">python-dotenv</code>)</li>\n<li>CLI args for feeds + database ID</li>\n<li>avoided hardcoding</li>\n</ul>\n<hr>\n<h3>6. Automation</h3>\n<p>Wrapped execution in a shell script:</p>\n<ul>\n<li>uses absolute paths</li>\n<li>logs per run (timestamped)</li>\n<li>avoids reliance on shell environment</li>\n</ul>\n<p>Scheduled with cron:</p>\n<ul>\n<li>learned macOS quirks (permissions, environment isolation)</li>\n<li>fixed via explicit paths + permissions</li>\n</ul>\n<p>Added system wake using <code class=\"language-text\">pmset</code> to ensure execution window.</p>\n<hr>\n<h3>7. Reliability fixes</h3>\n<ul>\n<li>handled LLM output variability</li>\n<li>added logging for observability</li>\n</ul>\n<hr>\n<h3>8. Packaging</h3>\n<ul>\n<li>created <code class=\"language-text\">requirements.txt</code></li>\n<li>documented setup in README</li>\n<li>pushed to GitHub</li>\n</ul>\n<hr>\n<h3>Final system</h3>\n<div class=\"gatsby-highlight\" data-language=\"textid=\"kj5p1f\"\"><pre class=\"language-textid=\"kj5p1f\"\"><code class=\"language-textid=\"kj5p1f\"\">RSS → extract → local LLM → structured output → Notion</code></pre></div>\n<p>Runs locally, no external LLM dependency, and produces a clean daily digest with minimal maintenance.</p>\n<hr>\n<p><strong>Key lessons:</strong></p>\n<ul>\n<li>control input size early</li>\n<li>don’t trust LLM output format blindly</li>\n<li>cron on macOS needs explicit handling</li>\n<li>keep config and secrets separate</li>\n</ul>\n<p>You can find the core code repository here: <a href=\"https://github.com/joshi-aparna/local-llm-blog-digest\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://github.com/joshi-aparna/local-llm-blog-digest</a></p>","fields":{"slug":"/local-llm-blog-digest/"},"frontmatter":{"title":"Local LLM Blog Digest","date":"2026, Apr 30","tags":["tech"],"memorydata":null,"practicedata":null,"img":{"childImageSharp":{"resize":{"src":"/static/aeb1a0b1f8efd09ea2d2a1320028f85b/f3583/blogdigest.png","height":675,"width":1200},"gatsbyImageData":{"layout":"fullWidth","placeholder":{"fallback":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8UlEQVQozy2R2XLbMAxF/dnpB6UPab+gz90yTpNIdqTIdixvsjZql0hiIdnKUwwe7mBwBrjAAomJDJEx1o6j+vPsr9bvLy/rMNq9eoHnB2G48/zg+WW12e4BicjgrV9pWMwY2zmt0xrX6+jVC97eNssnPwi3338ut7uDvwqXT174vgMgY5wxjtlqjTNcNX2S5FKBta6qm7IUzjkiTrOyqlt3izwTbTM454ZRxvGZ2PyHSUnUiueVbJbWWVozWwB+fHz2/IDIAtIxTcq2cc6Jql6tg3GUCLQgdhx9M/6DGpquk6doTGM1jBMAvW/C/XGvFYqujcThVKeG3cOXr3d3n8IwstbdPJeRPf1mLQGtuOo6BzYW0Xq/LuFrbqwbtb50hZg655y/eru//3w8nZntDBdVd8lqKQGQrmmW5QUgT6N++nHwlmdA6vspSbIsF4g8DLIUTdP2s2dm2/VDUZREzGzrpmvbno0lYtGUoqmMdcM4nS/XLC+JTVU1x9NFVLUGXCAZZsfsAC2imU9gHJEldkQO6abJGeuILd7qxs7fUhoXiLYox2vaJtembrSopn2cxXGWF72o1PFYfHxct7sky/sg/NjuLvEh22zPQkitaQHIGhjQ4PwSo4GVwknCJLUGUppuGqRCAJYKpYRpAqnx3+S/NBNnErACA4YAAAAASUVORK5CYII="},"images":{"fallback":{"src":"/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/56785/blogdigest.png","srcSet":"/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/0dee1/blogdigest.png 750w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/8beaa/blogdigest.png 1080w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/a4198/blogdigest.png 1366w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/56785/blogdigest.png 1672w","sizes":"100vw"},"sources":[{"srcSet":"/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/ba386/blogdigest.avif 750w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/8adb1/blogdigest.avif 1080w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/a3131/blogdigest.avif 1366w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/bc8d5/blogdigest.avif 1672w","type":"image/avif","sizes":"100vw"},{"srcSet":"/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/a66aa/blogdigest.webp 750w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/65dd5/blogdigest.webp 1080w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/f9724/blogdigest.webp 1366w,\n/blog/static/aeb1a0b1f8efd09ea2d2a1320028f85b/52a32/blogdigest.webp 1672w","type":"image/webp","sizes":"100vw"}]},"width":1,"height":0.562799043062201}}}}}},"pageContext":{"slug":"/local-llm-blog-digest/","previous":{"fields":{"slug":"/smb_reparse_point/"},"frontmatter":{"title":"Working with SMB and Containers on Windows","tags":["tech"],"img":{"childImageSharp":{"gatsbyImageData":{"layout":"fullWidth","placeholder":{"fallback":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACy0lEQVQoz02RW0hTcRzHz2MPPUTQhShsk+maIihWkillYJoaFWhSTYezKIwKXYG+aDJjEQWWdkF7cMOWhVFemuB0FzfXtnPOjhPnpm5u5zJT1KU557ZzduLsqARf/m+f//f7+36BfnhlaPLP6HTQ6KFM7vCv+bDZG7EwioIoBWIU+1rRbVl8pNlLmuajBncYGLKva1whrSs4DBN6L23CaTNBW/00uEBbfQwJ4zEbHoP/E4TFQCxm9pGAeurv6Ezk3c+pBlHec4mwVFglFlc114pkjZL3KkevZWlkemPcE7b4SAijYJxiYQiLWVEKGHZsKIwLFVJl4830nuojqZx9PM6BNM5eUUn6ix5Dlx79Aa+MTAfHPWEQjSIENUFQtvgXIEoBakewF1wRSeUyoQBsFoiLeB11md2S1NYHOY/bv78ZcHw24ip7YGwuBGOkfSE2QWxfAWIUoJvd+gatltW/bbqVMVCXXFPM1b88rZGmtFRn3n4mb+2zK424anLNMBcGUQrBKYRxjsE4BWEUoHFuKgzEtSdtN66eb7medOEk587lE/eKEysvpYkaP3SqZ1X2gNEdhjAGQ3AmMwPHVwC0rpB8jCiufV1+LrG++OjZTF5udnr2KX5BjqCoRvpUYVDo5geQVf1MiO0sDu/crDT6Gz6qBYWVJWcS7+cfS+YlHOTyDx3npvATkvJKxTJla59drvP12djaIiDbOev8sF2VU9W0h5uRxd8vzD1cflHwqDpfcreg4kpWYVmF5FV3h2ryq4kYnAiMOjcZGCVhjDmYmeqLyd+l8ci6dU1tnzqV/QhkXPY7lhfdcx631fl7fCZgmd+y+qIgSkIYCbOZd+Eh+5rWtWVFaWSRti/RkJ+2oIwggkb8NELQCBGLi0LYkeKxGdhHAYNIYHhqXesMjru3LN4I5IvAWNSGR21YFMZIGCN364V3PEGUIc1e8h9lfjbcWV6LJgAAAABJRU5ErkJggg=="},"images":{"fallback":{"src":"/blog/static/ad714188f072dd0a5343d3ff640f2ab0/6e90d/smb_reparse.png","srcSet":"/blog/static/ad714188f072dd0a5343d3ff640f2ab0/ea847/smb_reparse.png 750w,\n/blog/static/ad714188f072dd0a5343d3ff640f2ab0/4a806/smb_reparse.png 1080w,\n/blog/static/ad714188f072dd0a5343d3ff640f2ab0/e2aad/smb_reparse.png 1366w,\n/blog/static/ad714188f072dd0a5343d3ff640f2ab0/6e90d/smb_reparse.png 1536w","sizes":"100vw"},"sources":[{"srcSet":"/blog/static/ad714188f072dd0a5343d3ff640f2ab0/57584/smb_reparse.webp 750w,\n/blog/static/ad714188f072dd0a5343d3ff640f2ab0/984df/smb_reparse.webp 1080w,\n/blog/static/ad714188f072dd0a5343d3ff640f2ab0/4a276/smb_reparse.webp 1366w,\n/blog/static/ad714188f072dd0a5343d3ff640f2ab0/7db9c/smb_reparse.webp 1536w","type":"image/webp","sizes":"100vw"}]},"width":1,"height":0.6666666666666666}}}}},"next":null}},"staticQueryHashes":["251720178","764694655"]}