Blog
Technical notes on web development, DevOps, and AI integration.
124 articles
- 11:45backend
Twelve entries locally, eight on the VPS
Hand-typed knowledge entries never reached the VPS. A repo-tracked seed file, upserted on every container start, keeps both databases converged.
TL;DR: Hand-typed knowledge entries lived only in the local database, so the VPS was missing four. The fix is a seed file plus a small seeder running on every container start, using INSERT ... ON DUPLICATE KEY UPDATE keyed on slug.
#mariadb#docker#python - 16:51backend
The Marker That Hid Between Two Chunks
A correct regex run against stream fragments matches nothing when the marker spans two deltas. Strip the joined answer instead.
TL;DR: A CTA_FLAG marker leaked into a Redis-cached chat answer because the model split it across two stream chunks, so the per-delta regex strip never matched a full pattern. The fix adds a cleanup pass on the joined answer right before caching. Lesson: any regex run per-chunk is blind to split patterns, so sanitize at the last point before persistence.
#sse#streaming#regex - 15:39ai
That Time a Control Flag Showed Up in the Chat Bubble
A raw CTA_FLAG: YES marker leaked into a live chat bubble. The strip regex only matched end-of-string, and the SSE stream bypassed it entirely.
TL;DR: A CTA_FLAG marker leaked into the UI because the model ignored placement instructions and SSE streaming sent chunks before the strip regex ran. The fix moved CTA data into a structured field on the knowledge source, rendered directly by the frontend. Lesson: never parse UI-critical data out of model prose; use structured fields.
#llm#streaming#sse - 10:40frontend
One Wheel Flick, Two Jobs: Scroll First, Flip Later
Adding wheel paging to my AI answers popup: a listener that never attached, passive-by-default events, edge detection, and reduced motion.
TL;DR: Building wheel-based answer navigation in a React popup hit two traps: the useEffect ran before the dialog content existed, so the listener attached to a null ref, and wheel listeners default to passive in most browsers, silently neutering preventDefault. Fixing both required dependency-aware attachment and an explicit non-passive listener. Scroll-edge detection plus prefers-reduced-motion CSS handles the rest.
#react#frontend#ux - 00:54frontend
Chat result rows, timeline style: metadata first
The time chip and tags in the chat popup were never a CSS problem. Metadata had to travel from SQL up to the DTO before the UI could dress up.
TL;DR: The popup rows looked flat because the API never sent published_at, tags, or categories—the fix started at the database, lifting metadata through repositories, DTOs, and SSE until the frontend had data to render. The existing TimelineRow component was reused across all three popup contexts instead of copying markup. Total cost: seven files, 214 lines added.
#react#pydantic#sqlmodel - 00:33ai
Prompt labels leaked into my AI chat: sanitize both ends
A RINGKAS label leaked into the chat bubble. The fix was not a stricter prompt but sanitizing input and output at both ends of the pipeline.
TL;DR: The author's chatbot kept leaking prompt labels like RINGKAS into answers, and adding prompt prohibitions never fixed it. They moved control to code: sanitizing knowledge-base text before injection and rendering output through react-markdown with rehype-sanitize allowlists. Lesson: prompts give rough direction only; reliable formatting enforcement belongs at input and output boundaries.
#llm-security#react#prompt - 21:51ai
What a Security Audit Found in My Chat AI
A security audit uncovered 11 findings in the chat AI pipeline, from cache keys to prompt injection.
#prompt-injection#redis#fastapi - 20:07ai
The follow-up question my AI chat couldn't answer
Chat follow-up questions without server sessions: history lives in localStorage, gets resent per ask, and the backend rebuilds the prompt under a char budget.
#fastapi#llm#stateless - 19:34ai
The AI chat now knows what I am working on
The blog's AI chat now answers 'what are you working on' from local commit history: a narrow port, a version namespace bump, and term mining.
#fastapi#redis#cache-invalidation