image.png

TL;DR — Two surfaces of AgentZero Lite expanded at once. The LLM layer went from a single local Gemma-4 to five backends (local Gemma, local Nemotron Nano 8B, Webnori, OpenAI, LM Studio, Ollama) behind one OpenAI-compatible interface. The modality layer grew a voice — a STT/TTS subsystem with four STT engines, two TTS engines, dual-VAD mic capture, and a live pipeline that reuses the same LLM gateway. Neither expansion needed a new architecture; both fell out of the same design principle that already defined the agent: one abstract interface, many implementations.

This is a follow-up to the foundation report — An LLM Is Not an Agent: Five Layers. That doc argued the agent is what surrounds the model. This doc shows what happens when you keep that frame and ask: what else can we slot behind the same interfaces?

The two surfaces that just expanded

flowchart LR
  subgraph Before
    A1[Local Gemma 4<br/>+ Nemotron]:::dim
    T1[Text only]:::dim
  end
  subgraph After
    A2[Local Gemma 4<br/>Local Nemotron<br/>Webnori<br/>OpenAI<br/>LM Studio<br/>Ollama]
    T2[Text + Voice<br/>STT × 4 / TTS × 2]
  end
  A1 -.expanded.-> A2
  T1 -.expanded.-> T2
  classDef dim fill:#f5f5f5,stroke:#999,color:#666

Same shell. Same actor topology. Same harness. The expansions land inside the boxes the field report drew.


Surface 1 — From one local LLM to five providers

The original story was self-built llama.cpp + LLamaSharp 0.26 running Gemma-4 / Nemotron on-device. That solved one question: can the loop work entirely offline? Yes. But it left the user stuck if they wanted to try a hosted model, or already had Ollama / LM Studio running on the same machine.

The expansion adds four external providers and unifies them behind one HTTP client:

Provider Mode Endpoint Reason to include
Webnori Hosted (free, public) OpenAI-compatible Zero-friction first try
OpenAI Hosted (paid) api.openai.com/v1 Frontier comparison
LM Studio Local localhost:1234/v1 Already-running local server
Ollama Local localhost:11434/v1 Most common local server

All four speak the OpenAI Chat Completions wire format, so a single OpenAiCompatibleProvider covers them. Only the base URL and credentials differ.

public interface ILlmProvider
{
    string ProviderName { get; }
    Task<List<LlmModelInfo>> ListModelsAsync(CancellationToken ct = default);
    Task<LlmResponse> CompleteAsync(LlmRequest request, CancellationToken ct = default);
    IAsyncEnumerable<LlmStreamChunk> StreamAsync(LlmRequest request, CancellationToken ct = default);
}

Source: Project/ZeroCommon/Llm/Providers/ILlmProvider.cs

The selection lives where the user expects it — a Local / External radio on the LLM settings tab. Picking External reveals provider + model + API-key fields; the chosen provider is persisted in LlmSettingsStore. A factory (LlmProviderFactory) returns the right ILlmProvider at runtime.

flowchart TD
  S[LLM tab<br/>Local · External] --> F[LlmProviderFactory]
  F -->|Local| LH[LLamaSharp host<br/>Gemma 4 / Nemotron / …]
  F -->|External| OAC[OpenAiCompatibleProvider]
  OAC -->|baseUrl + apiKey| W[Webnori]
  OAC -->|baseUrl + apiKey| O[OpenAI]
  OAC -->|baseUrl| LMS[LM Studio]
  OAC -->|baseUrl| OLL[Ollama]

Where this connects back to AIMODE

The foundation report described AIMODE's dual backend — Gemma 4 via GBNF, Nemotron via the Llama-3.1 chat template, both behind IAgentToolHost. The provider expansion follows the same principle, one layer up:

Layer Interface Implementations
Tool host IAgentToolHost WorkspaceTerminalToolHost
Tool-call backend (AIMODE) AgentToolLoop / ExternalAgentToolLoop Gemma-GBNF, Nemotron-template
LLM provider (new) ILlmProvider OpenAiCompatibleProvider × 4 hosts, local LLamaSharp host

Same shape, different layer. Adding a fifth provider tomorrow is a config entry, not a refactor.