Skip to content

Commit 428f895

Browse files
authored
Merge pull request #3 from dydanz/blog/nanoclaw-pt-4
blog: NanoClaw Part 4 — LLM Routing
2 parents 9e51980 + 5f35cb1 commit 428f895

8 files changed

Lines changed: 657 additions & 4 deletions

File tree

package-lock.json

Lines changed: 309 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"react-scripts": "^5.0.1",
2222
"react-test-renderer": "^16.14.0",
2323
"react-twitter-embed": "^3.0.3",
24+
"remark-gfm": "^1.0.0",
2425
"sass": "^1.65.1"
2526
},
2627
"scripts": {

public/blogs/2026-04-19-nanoclaw-pt-2.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ repo: https://github.com/dydanz/kandangkambing
1212

1313
---
1414

15-
In [Part 1](01-why-i-built-this.md), I talked about why I built NanoClaw — the itch, the experiment, and the moment a weekend project grew legs. This post is about how the system is actually put together. Not the aspirational version, but the real one: what's in the repo, how the pieces connect, and where I made trade-offs I'm still thinking about.
15+
In [Part 1](/blog/why-i-built-nanoclaw), I talked about why I built NanoClaw — the itch, the experiment, and the moment a weekend project grew legs. This post is about how the system is actually put together. Not the aspirational version, but the real one: what's in the repo, how the pieces connect, and where I made trade-offs I'm still thinking about.
1616

1717
If you want to follow along, the code is at [github.com/dydanz/kandangkambing](https://github.com/dydanz/kandangkambing). The repo is Python, built on top of NanoClaw's lightweight agent framework, with specs under `docs/specs/`, agent configurations in `.claude/`, and the core engine under `nanoclaw/`.
1818

public/blogs/2026-04-19-nanoclaw-pt-3.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ repo: https://github.com/dydanz/kandangkambing
1212

1313
---
1414

15-
The [previous post](02-architecture.md) described the architecture — agents, memory, worktrees, the workflow engine. That's the blueprint. This post is the actual construction site. I'm going to walk through what happens, step by step, when you type a message in Discord and wait for a PR to show up.
15+
The [previous post](/blog/nanoclaw-architecture) described the architecture — agents, memory, worktrees, the workflow engine. That's the blueprint. This post is the actual construction site. I'm going to walk through what happens, step by step, when you type a message in Discord and wait for a PR to show up.
1616

1717
I'm using the health check endpoint as the example because it's simple, self-contained, and — more importantly — because it's one of the first features I actually ran through the system end-to-end. The early PRs in [kandangkambing](https://github.com/dydanz/kandangkambing) were built this way. There are 10 PRs in the repo now (9 merged, 1 open), and following this flow is how most of them got there.
1818

@@ -328,4 +328,4 @@ In the next post, I'll dig into the LLM routing layer — why GPT-4o writes bett
328328

329329
---
330330

331-
*Part 3 of 7 — [← Part 2: The Architecture](/blog/nanoclaw-architecture) · Part 4: Coming Soon*
331+
*Part 3 of 7 — [← Part 2: The Architecture](/blog/nanoclaw-architecture) · [Part 4: LLM Routing →](/blog/nanoclaw-llm-routing)*

public/blogs/2026-04-19-nanoclaw-pt-4.md

Lines changed: 294 additions & 0 deletions
Large diffs are not rendered by default.

src/blogs/manifest.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
// ALWAYS ADD NEW BLOGS (BASED ON DATE) TO THE TOP OF THE LIST
99

1010
const blogManifest = [
11+
{
12+
title: "LLM Routing: Why I Use Three Different AI Providers (And How I Stopped Overpaying)",
13+
date: "2026-04-19",
14+
slug: "nanoclaw-llm-routing",
15+
excerpt: "Not every task needs the same model. How NanoClaw picks between Claude, GPT-4o, and Gemini — and the $40 mistake that made cost tracking non-negotiable.",
16+
file: "/blogs/2026-04-19-nanoclaw-pt-4.md"
17+
},
1118
{
1219
title: "From Discord Command to GitHub PR: What Actually Happens",
1320
date: "2026-04-21",

src/containers/blogs/Blog.scss

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,47 @@
209209
color: $textColorDark;
210210
}
211211

212+
/* Tables inside blog posts */
213+
.blog-post-content table {
214+
border-collapse: collapse;
215+
width: 100%;
216+
margin: 1.5em 0;
217+
font-size: 15px;
218+
}
219+
220+
.blog-post-content th,
221+
.blog-post-content td {
222+
border: 1px solid $lightBoxShadowDark;
223+
padding: 8px 12px;
224+
text-align: left;
225+
color: $subTitle;
226+
}
227+
228+
.blog-post-content th {
229+
background: $lightBackground3;
230+
color: $titleColor;
231+
font-weight: 600;
232+
}
233+
234+
.blog-post-content tr:nth-child(even) td {
235+
background: $lightBackground2;
236+
}
237+
238+
.blog-post-content.dark-mode table th {
239+
background: lighten($darkBackground, 6%);
240+
color: $textColorDark;
241+
border-color: lighten($darkBackground, 15%);
242+
}
243+
244+
.blog-post-content.dark-mode table td {
245+
color: darken($textColorDark, 10%);
246+
border-color: lighten($darkBackground, 15%);
247+
}
248+
249+
.blog-post-content.dark-mode table tr:nth-child(even) td {
250+
background: lighten($darkBackground, 4%);
251+
}
252+
212253
.blog-post-meta {
213254
font-size: 13px;
214255
color: $subTitle;

src/containers/blogs/BlogPost.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, {useState, useEffect, useContext} from "react";
22
import {useParams, useHistory} from "react-router-dom";
33
import ReactMarkdown from "react-markdown";
4+
import remarkGfm from "remark-gfm";
45
import Header from "../../components/header/Header";
56
import Footer from "../../components/footer/Footer";
67
import ScrollToTopButton from "../topbutton/Top";
@@ -53,7 +54,7 @@ function BlogPostContent() {
5354
<div className={isDark ? "blog-post-content dark-mode" : "blog-post-content"}>
5455
<p className="blog-post-meta">{post.date}</p>
5556
<h1 className="blog-post-title">{post.title}</h1>
56-
{loading ? <p>Loading...</p> : <ReactMarkdown>{content}</ReactMarkdown>}
57+
{loading ? <p>Loading...</p> : <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>}
5758
</div>
5859
</div>
5960
);

0 commit comments

Comments
 (0)