Skip to content

Migrate from per-provider chatbot classes to a unified LLM interface #99

@MaleicAcid

Description

@MaleicAcid

Problem

chatbot.py maintains three separate classes (GPTBot, ClaudeBot, GeminiBot), each wrapping a different SDK. This has led to:

  • Duplicated logic (~580 lines): retry, fee tracking, auth, and temperature clamping are implemented three times.
  • Bugs from drift: ClaudeBot mutates the input message list via messages.pop(0), silently losing the system prompt on retries. GeminiBot has its entire try/except commented out, so any API error crashes instead of retrying.
  • Incomplete features: max_async is accepted but never enforced; route_chatbot() supports "openai:" and "anthropic:" prefixes but not "google:".
  • Maintenance cost: every new provider requires a new class, a new SDK dependency, and a new copy of all the above logic.

Goal

Replace the three classes with a single implementation backed by a unified LLM interface. The prompt layer (prompter.py, agents.py, translate.py) stays unchanged — all options below use the standard messages format.

Options

Option A: OpenAI SDK + provider-compatible endpoints

Google and Anthropic now offer OpenAI-compatible endpoints. We could keep only the openai SDK and remove anthropic and google-genai from dependencies. One ChatBot class handles all providers by varying base_url and api_key.

  • Pros: zero new dependencies (removes two); no extra cost or latency.
  • Cons: compatibility endpoints are relatively new and may not expose all provider-specific features; retry and fee tracking still maintained by us.

Option B: LiteLLM

LiteLLM provides litellm.acompletion() across 100+ providers with built-in retry, fallback, and usage tracking. Its core dependencies (openai, httpx, tiktoken, pydantic, etc.) largely overlap with what openlrc already has, so the actual new footprint is small.

  • Pros: built-in retry/fallback and cost tracking; 100+ providers out of the box; actively maintained (42k+ stars).
  • Cons: adds a dependency; one more abstraction layer between us and the provider.

Option C: OpenRouter (service)

OpenRouter is a cloud proxy with an OpenAI-compatible endpoint for 300+ models. openlrc already partially uses it (GPTBot._resolve_client_settings() has OpenRouter fallback, README recommends OPENROUTER_API_KEY).

  • Pros: simplest code change — keep only GPTBot, remove the other two classes; automatic provider-side fallback.
  • Cons: OpenRouter charges a 5.5% fee on every credit purchase (with a $0.80 minimum). While per-token inference prices match the providers directly, this upfront fee is unavoidable and adds up for heavy users. Also adds network latency (extra hop through OpenRouter's proxy), and creates a hard dependency on a single commercial service — if OpenRouter goes down, all LLM calls fail.

Discussion

  1. Which option best fits the project?
  2. Are there provider-specific features (e.g. Gemini safety settings) that must be preserved?
  3. Should models.py be kept for fee estimation, or delegate cost tracking to the chosen solution?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions