Mert Özen Further With Every Line

Blog

Mert Özen's blog posts on software development, backend, frontend, and DevOps.

Series
9 posts found
Why Are AI Interfaces Different? Streaming, Uncertainty and the Angular + ASP.NET Core Architecture
Artificial Intelligence PART 01

Why Are AI Interfaces Different? Streaming, Uncertainty and the Angular + ASP.NET Core Architecture

What sets an AI interface apart from a classic CRUD screen is that the answer arrives in pieces, differs every time and can stop halfway: the same question got two different books, a 6 and 13 second time to first token and 2.5 times different token usage in two runs. In this post we set up the architecture between Angular, ASP.NET Core and the model, show why the API key must never enter the bundle, and define the event vocabulary of /chat/sse.

14 Nov 2026 12 min 2
Streaming Chat in Angular: Reading SSE with fetch, resource stream and Rendering Token by Token with Signals
Artificial Intelligence PART 02

Streaming Chat in Angular: Reading SSE with fetch, resource stream and Rendering Token by Token with Signals

Since EventSource cannot send a POST body, we read the LLM stream in Angular with fetch, ReadableStream and TextDecoderStream and write the events into signals. In this post we measure a UTF-8 character split at a chunk boundary, a Stop button that reaches the server through AbortController, requestAnimationFrame halving DOM writes, a @for track mistake that recreates the element on every update, and a resource stream that reports resolved while still streaming.

14 Nov 2026 17 min 2
AI Chat State and Error Handling: A State Machine, Partial Answers, Timeouts, 429 and Regenerate
Artificial Intelligence PART 03

AI Chat State and Error Handling: A State Machine, Partial Answers, Timeouts, 429 and Regenerate

What makes an AI chat reliable is an explicit state machine and telling error types apart: 400 ProblemDetails, 429 with Retry-After, a first-token timeout, an error event mid-stream and a dropped connection. In this post we mark half answers instead of deleting them, separate aborts from errors, show the dev proxy keeping the stream open for 90 seconds after the API process died and a ping-based silence timer catching it in 15, and tie regenerate to the server-side ConversationId history.

14 Nov 2026 14 min 2
Rendering LLM Output Safely: Streaming Markdown, Sanitization, Code Blocks and Citations
Artificial Intelligence PART 04

Rendering LLM Output Safely: Streaming Markdown, Sanitization, Code Blocks and Citations

Because LLM output is untrusted input, Angular renders it through a three-layer pipeline of marked, DOMPurify and Angular's sanitizer, with no images and a link allowlist. In this post we cut script time to a third by freezing completed blocks, show that a rule trusting empty lines split a list in two and that autolink made a half URL clickable, and render code blocks with a component and [n] citations validated against the source list.

14 Nov 2026 15 min 2
Tool Calls, Approval Flows and Generative UI: Letting the Assistant Choose Components
Artificial Intelligence PART 05

Tool Calls, Approval Flows and Generative UI: Letting the Assistant Choose Components

Showing the assistant's tool calls, tying a sensitive action to a time-limited single-use approval card and letting the model pick an allowed component instead of writing text is done in Angular with typed SSE events and a validating component registry. In this post we show a small model claiming it displayed cards that never appeared, an error text arriving at the UI as props and client validation stopping it, and we look at Hashbrown and its postinstall telemetry.

14 Nov 2026 15 min 2
Connecting Through Standard Protocols: Choosing Between AG-UI, AI SDK UI and Our Own SSE Contract
Artificial Intelligence PART 06

Connecting Through Standard Protocols: Choosing Between AG-UI, AI SDK UI and Our Own SSE Contract

Choosing between your own SSE contract, AG-UI and AI SDK UI comes down to the backend, the number of clients and the bundle and wire cost you accept. In this post we connect the same assistant to Angular over all three, and measure AG-UI's defaults sending the model's reasoning to the browser in a 368 KB stream, the two protocol libraries adding 500-680 kB to the page, and an abort arriving as RUN_ERROR.

14 Nov 2026 16 min 2
AI UX and Accessibility: Perceived Waiting, aria-live, Keyboard, Feedback and Transparency
Artificial Intelligence PART 07

AI UX and Accessibility: Perceived Waiting, aria-live, Keyboard, Feedback and Transparency

A streamed AI answer should reach screen readers as one LiveAnnouncer announcement when the turn ends, not in pieces; focus should stay in the input, and the waiting time and an AI label should be visible. In this post we measure aria-live producing 70 changes for three sentences, the Send/Stop swap dropping focus to body unnoticed by axe-core, fix a contrast issue and wire thumbs up/down feedback to the backend.

14 Nov 2026 12 min 2
Testing a Streaming AI UI: Fake Streams, Vitest Fake Timers, MSW and Playwright
Artificial Intelligence PART 08

Testing a Streaming AI UI: Fake Streams, Vitest Fake Timers, MSW and Playwright

A streaming AI UI is tested deterministically with fake AsyncGenerators that take the model out of the loop, fake timers, MSW responses with ReadableStream bodies and Playwright tests using page.route. In this post we show instanceof DOMException turning a cancellation into an error in the test environment, MSW's sse() handler not fitting a fetch stream, and a ping-less fake stream hitting the 15 s silence timer before the 30 s timeout; 18 tests finish in under 2 seconds.

14 Nov 2026 16 min 2
AI in the Browser and Angular 22's Experimental AI Tooling: Built-in AI APIs and WebMCP
Artificial Intelligence PART 09

AI in the Browser and Angular 22's Experimental AI Tooling: Built-in AI APIs and WebMCP

Chrome's built-in AI APIs and Angular 22's experimental WebMCP functions should only be used with feature detection, a time limit and a server path that always works. In this post we show Summarizer returning downloadable, unavailable or nothing across four Chromium 158 setups, produce a review summary on the device first and on the server otherwise, hit NG0203 from inject() after await in a WebMCP tool, and see Angular stay silent in unsupported browsers.

14 Nov 2026 15 min 2
Blog Map