Skip to main content
Source Code:
Primary API: create_client() factory function Module: gaia.llm Imports:
  • from gaia.llm import create_client (preferred)
  • from gaia.llm import LLMClient, NotSupportedError

Overview

The LLM client package provides a unified interface for generating text from multiple LLM backends using a provider pattern. Each provider implements the abstract LLMClient interface, with optional methods raising NotSupportedError when unavailable. Key Features:
  • Factory-based client creation with create_client()
  • Two providers: Lemonade (local AMD-optimized, plus models configured behind Lemonade such as on-prem gateways) and Claude
  • Abstract base class for type safety and extensibility
  • Graceful handling of unsupported features via NotSupportedError
  • Streaming and non-streaming generation
  • Backward-compatible use_claude flag
Provider Capabilities:
Methods marked with ✗ raise NotSupportedError when called on that provider. Cloud models served through Lemonade have their own capabilities; selecting Lemonade does not imply that every remote model supports every operation.

Removed adapters and migration

The direct openai and litellm adapters were removed because they discarded native tool calls. The litellm installation extra is removed too. The openai Python dependency remains: Lemonade uses it for compatible HTTP requests. provider="openai", provider="litellm", and use_openai=True raise ValueError with migration guidance before importing a provider or issuing requests. use_chatgpt=True in the agent, chat/audio/talk SDK chain and the legacy --use-chatgpt CLI flag also fail explicitly. These flags are retained only as migration-error shims; they do not select another backend or ignore the setting. Configure the upstream model in Lemonade, then pass the Lemonade server URL and its catalog model ID. Do not substitute a raw upstream endpoint for Lemonade’s URL: a generic OpenAI-compatible service does not implement Lemonade’s catalog and lifecycle API. See gateway migration. Historical release notes describe the providers that shipped in those releases.

Requirements

Functional Requirements

  1. Factory Pattern
    • create_client() factory function for client creation
    • Explicit provider selection via provider parameter (“lemonade”, “claude”)
    • Backward-compatible use_claude flag
    • Auto-detection of provider from flags when provider not specified
    • Default to Lemonade provider when no flags set
    • Retired names ("openai", "litellm") and a true use_openai flag raise ValueError with migration guidance before any provider is imported
  2. Abstract Interface
    • LLMClient ABC defines unified interface
    • provider_name property returns provider name
    • Required methods (all providers must implement):
      • generate() - Text completion
      • chat() - Chat completion with message history
    • Optional methods (raise NotSupportedError if not implemented):
      • embed() - Generate embeddings
      • vision() - Vision/image understanding
      • get_performance_stats() - Performance statistics
      • load_model() - Load a model
      • unload_model() - Unload current model
  3. Provider Implementations
    • LemonadeProvider: Full support for all methods, connects to local Lemonade server
    • ClaudeProvider: generate, chat, vision, get_performance_stats (last-call usage)
    • All providers support streaming and non-streaming modes
  4. Error Handling
    • NotSupportedError raised for unsupported methods
    • Clear error messages indicating provider and unsupported method
    • Connection errors handled by underlying provider implementations

Non-Functional Requirements

  1. Performance
    • Lazy provider loading via importlib (load only when needed)
    • Minimal overhead from abstraction layer
    • Streaming support across all providers
    • Local models run with their model card’s sampling, else temperature 0.1 with penalties; cloud models keep the client default
  2. Reliability
    • Type safety through ABC pattern
    • Graceful handling of unsupported features
    • Clear error messages for provider capabilities
    • Provider-specific connection management
  3. Usability
    • Simple factory function interface
    • Backward compatibility with existing code
    • Consistent API across all providers
    • Clear documentation with examples

API Specification

Package Structure

Package Exports (__init__.py)

VLMClient is also re-exported from gaia.llm — see vlm-client for its API.

Factory Function (factory.py)


Abstract Base Class (base_client.py)


NotSupportedError (exceptions.py)


Provider Implementations

LemonadeProvider (providers/lemonade.py)

Full feature support - implements all methods.

ClaudeProvider (providers/claude.py)

Partial support - generate, chat, vision, get_performance_stats.

Implementation Details

Provider Selection Logic

The factory function auto-detects the provider based on parameters:

Lazy Provider Loading

Providers are loaded dynamically using importlib to avoid importing unnecessary dependencies:

NotSupportedError Pattern

Optional methods raise NotSupportedError by default in the ABC:
Providers override only the methods they support:

Sampling Defaults

A local model whose card publishes sampling settings runs with them. These live in MODEL_SAMPLING_PROFILES in lemonade_client.py, keyed by Lemonade model id. For example, Qwen3-30B-A3B-Instruct-2507-GGUF gets temperature=0.7, top_p=0.8, top_k=20, min_p=0 and presence_penalty=1.0, per its model card. A card that gives separate thinking and non-thinking profiles registers both. The thinking mode comes from requested_thinking(): the caller’s chat_template_kwargs={"enable_thinking": ...} when it sets one, else GAIA’s choice for the model (ModelRequirement.thinking). The provider picks the sampling profile from it and LemonadeClient.chat_completions sends it as the switch, so the sampling always matches the mode the model runs in. Qwen3.6-35B-A3B-GGUF runs with thinking on and gets its card’s thinking profile for coding and precise work. Memory’s short JSON side calls (extraction, consolidation, reconciliation, skill distillation) pass no_thinking_kwargs(model), which turns thinking off for such a model: reasoning is billed against max_tokens, so with it on they reason to the cap and return no answer. Every other local model gets LOCAL_SAMPLING_DEFAULTS: temperature=0.1 plus frequency, presence and repeat penalties, so it does not loop on tables and paragraphs. Cloud models get neither, because near-greedy sampling sends a reasoning model’s thinking into a runaway. They keep the client default (0.7). Any sampling argument the caller passes is sent as given.
top_k, min_p, repeat_penalty and repeat_last_n are llama.cpp-native. When streaming, they travel in the OpenAI SDK’s extra_body and land at the top level of the request body.

Provider-Specific Implementation

LemonadeProvider wraps the low-level LemonadeClient:
ClaudeProvider uses the Anthropic SDK:

Native tool-call transport

The shared agent consumes the provider’s string response. Lemonade preserves native calls in the __tool_calls__ sentinel envelope, including call IDs, function names and arguments; it does not replace a tool-only response with empty assistant text. Streaming deltas are accumulated before dispatch so a partial argument fragment cannot execute as a complete call. The agent turns results back into the corresponding tool messages for the next model request. Provider usage is available through get_last_usage() when the backend reports it. Missing usage is not proof of zero tokens or zero cost. Per-turn timing and usage records are owned by agents/base/turn_metrics.py.

Error boundaries

Configuration errors fail before inference. Lemonade transport errors are classified by the Lemonade provider into typed failures with user-facing guidance. Authentication and upstream service failures must remain attributable to the configured destination; no implicit retry with a different model or provider is permitted. Optional dependencies must not be required merely to reject a removed provider name.

Testing Requirements

Unit Tests

File: tests/unit/test_llm_client_factory.py
File: tests/unit/test_removed_llm_providers.py — retired names and flags are rejected across the factory, SDK, base agent and CLI, before provider import or model initialization.

Regression coverage

  • test_llm_client_factory.py: supported selection and shared interface.
  • test_removed_llm_providers.py: rejected legacy names and flags across the factory, SDK, base agent and CLI, before model initialization.
  • unit/test_lemonade_client_http.py: supported Lemonade operations and their request shapes, against mocked HTTP.
  • test_lemonade_client.py: the same operations against a live Lemonade Server.
  • test_lemonade_cloud_models.py: local/cloud lifecycle separation.
  • test_parallel_tool_calls.py: preservation and execution of native calls.
The unit workflow triggers for changes to shared source and the packaged chat agent. Tests must exercise the retained route as well as migration failures.

Integration Tests

File: tests/integration/test_llm_providers.py

Dependencies

Required Packages

Import Dependencies

Factory (factory.py):
Base Client (base_client.py):
LemonadeProvider (providers/lemonade.py):
ClaudeProvider (providers/claude.py):

Usage Examples

Example 1: Basic Usage with Factory

Example 2: Explicit Provider Selection

Example 3: Handling Unsupported Features

Example 4: Chat with Message History

Example 5: Streaming Responses

Example 6: Embeddings (Lemonade only)

Example 7: Vision (Lemonade and Claude only)

Example 8: Model Management (Lemonade only)

Example 9: Remote Lemonade Server

Example 10: System Prompts


Third-Party LLM Integration

The Lemonade provider sends inference over OpenAI-compatible endpoints, but before a local-model request it also calls Lemonade’s model-lifecycle API (for example /load). A generic OpenAI-compatible server is therefore not a drop-in replacement: configure the third-party or on-prem model in a Lemonade server and point GAIA at that server. See gateway migration.

Inference Endpoints

GAIA sends inference requests in the OpenAI-compatible shapes below.

Completions Endpoint

Default: POST /v1/completionsUsed for pre-formatted prompts

Chat Completions Endpoint

POST /v1/chat/completionsUsed for structured conversations with message history

Completions Endpoint

Chat Completions Endpoint


Configuration

Point GAIA at the Lemonade server that fronts your model:
Linux
Windows (PowerShell)
Windows (CMD)
URL Normalization: LemonadeClient automatically appends /api/v1 if not present:
  • http://localhost:8080 → http://localhost:8080/api/v1
  • If your server uses /v1 instead, provide the full path: http://localhost:8080/v1

Example Integration


Troubleshooting

Problem: Requests fail because the server can’t be reached (connection refused, DNS, or TLS)Solutions:
  1. Verify the server is running:
  2. Check firewall settings
  3. Ensure correct base URL format
  4. Test with explicit base URL:
Problem: Requests fail with HTTP 404Solutions:
  1. Verify API path structure: /v1 vs /api/v1
  2. Confirm base_url points at the Lemonade server, not the raw upstream endpoint
  3. Use chat method explicitly if needed:
Problem: Model errors or “model not loaded”Solutions:
  1. Specify model explicitly:
  2. List the models the server exposes:
  3. Use the exact model ID from that catalog
Problem: Streaming responses not workingSolutions:
  1. Verify the server supports Server-Sent Events (SSE)
  2. Check Content-Type headers: text/event-stream
  3. Test non-streaming first:
  4. Enable debug logging:

Documentation Updates Required

docs/sdk/sdks/llm.mdx

Add to LLM Section:

LLMClient Technical Specification