Skip to main content

LLM Providers Module Overview

This document describes the overall architecture of the LLM provider subsystem in Elftia. The subsystem is responsible for managing multi-provider configurations, API key pools, model discovery, request routing, and the Completion call pipeline.


File Locations

ModulePath
CompletionServicepackages/desktop/app/main/services/capabilities/llm/completion/CompletionService.ts
ApiKeyPoolServicepackages/desktop/app/main/services/capabilities/llm/completion/ApiKeyPoolService.ts
LLMConfigServicepackages/desktop/app/main/services/capabilities/llm/config-service/LLMConfigService.ts
ProviderManagerpackages/desktop/app/main/services/capabilities/llm/config-service/ProviderManager.ts
ModelDiscoveryManagerpackages/desktop/app/main/services/capabilities/llm/config-service/ModelDiscoveryManager.ts
ConfigIOManagerpackages/desktop/app/main/services/capabilities/llm/config-service/ConfigIOManager.ts
AgentModelsManagerpackages/desktop/app/main/services/capabilities/llm/config-service/AgentModelsManager.ts
ThinkingResolverpackages/desktop/app/main/services/capabilities/llm/completion/ThinkingResolver.ts
StreamHandlerpackages/desktop/app/main/services/capabilities/llm/completion/StreamHandler.ts
DirectApiHandlerpackages/desktop/app/main/services/capabilities/llm/completion/DirectApiHandler.ts
ToolHandlerpackages/desktop/app/main/services/capabilities/llm/completion/ToolHandler.ts
TransformerHandlerpackages/desktop/app/main/services/capabilities/llm/completion/TransformerHandler.ts
ProviderSearchInjectorpackages/desktop/app/main/services/capabilities/llm/completion/ProviderSearchInjector.ts
NativeSearchInjectorpackages/desktop/app/main/services/capabilities/llm/completion/NativeSearchInjector.ts
URL Builderpackages/desktop/app/main/services/capabilities/llm/completion/url-builder.ts
Header Builderpackages/desktop/app/main/services/capabilities/llm/completion/header-builder.ts
Message Converterpackages/desktop/app/main/services/capabilities/llm/completion/message-converter.ts
Provider Presetspackages/desktop/app/shared/provider-presets.ts
IPC Routerspackages/desktop/app/main/services/routers/llm/

Architectural Context

graph TB
subgraph Frontend ["Frontend (Renderer)"]
UI[Provider Settings UI]
ChatUI[Chat Interface]
end

subgraph IPCLayer ["IPC Layer"]
PR[ProviderRouter]
AKR[ApiKeyRouter]
CR[CompletionRouter]
RCR[RouterConfigRouter]
TR[TransformerRouter]
MPR[ModelParametersRouter]
end

subgraph LLMConfigService ["LLMConfigService (Delegate Pattern)"]
PM[ProviderManager]
MDM[ModelDiscoveryManager]
CIOM[ConfigIOManager]
AMM[AgentModelsManager]
end

subgraph CompletionService ["CompletionService (Facade Pattern)"]
DAH[DirectApiHandler]
SH[StreamHandler]
TH[ToolHandler]
THR[TransformerHandler]
TR2[ThinkingResolver]
end

AKPS[ApiKeyPoolService]

subgraph ExternalAPIs ["External APIs"]
OpenAI[OpenAI API]
Anthropic[Anthropic API]
Gemini[Gemini API]
Azure[Azure OpenAI API]
Others[Other Providers...]
end

subgraph Storage ["Storage"]
SQLite[(SQLite)]
JSON[(llms-config.json)]
end

UI --> PR
UI --> AKR
ChatUI --> CR

PR --> PM
AKR --> AKPS
CR --> CompletionService

PM --> SQLite
PM --> JSON
MDM --> SQLite
MDM --> JSON

CompletionService --> AKPS
CompletionService --> LLMConfigService
DAH --> OpenAI
DAH --> Anthropic
DAH --> Gemini
DAH --> Azure
SH --> OpenAI
SH --> Anthropic
SH --> Gemini
SH --> Azure

Data Structures

Core Types

// API format enum
type ApiFormat = 'openai' | 'anthropic' | 'google' | 'azure-openai' | 'openai-response';

// LLM provider
interface LLMProvider {
id: string; // Unique identifier
name: string; // Display name
apiFormat?: ApiFormat; // API format (preferred field)
chatApiFormat?: ApiFormat; // Legacy API format field
apiType?: string; // Legacy type field
api_base_url?: string; // API base URL
api_key: string; // Single key (legacy)
models: string[]; // Model ID list
modelConfigs?: ModelConfig[]; // Per-model detailed config
modelGroups?: ModelGroup[]; // Model groups
modelsEndpoint?: string; // Model discovery endpoint
enabled: boolean; // Whether enabled
transformer?: TransformerConfig; // Transformer chain config
icon?: string; // Icon
website?: string; // Official website
docsUrl?: string; // Documentation link
defaultSettings?: CompletionSettings; // Default completion parameters
codingPlan?: CodingPlanConfig; // Coding Plan config
isSystem?: boolean; // System built-in provider
presetId?: string; // Preset template ID
createdAt: string;
updatedAt: string;
}

// Completion request options
interface CompletionOptions {
providerId: string; // Provider ID
model: string; // Model ID
messages: SimpleChatMessage[]; // Message list
maxTokens?: number; // Max token count
temperature?: number; // Temperature
stream?: boolean; // Whether streaming
thinkLevel?: ThinkLevel; // Thinking level
nativeSearchAugmentation?: NativeSearchAugmentation; // Native search augmentation
sessionId?: string; // Session ID (for API Key Pool affinity)
}

// API key pool entry
interface ApiKeyEntry {
id: string; // Unique ID
providerId: string; // Owning provider
label?: string; // Display label
apiKey: string; // Key value
enabled: boolean; // Whether enabled
weight: number; // Weight (1-100)
}

Data Flow Overview

Completion Request Pipeline

sequenceDiagram
participant Frontend as Frontend
participant CS as CompletionService
participant LLC as LLMConfigService
participant AKP as ApiKeyPoolService
participant Handler as API Handler
participant API as Provider API

Frontend->>CS: complete() / completeStream()
CS->>LLC: resolveRoutedModel(providerId, model)
LLC-->>CS: actualProviderId + actualModel
CS->>LLC: getProvider(actualProviderId)
LLC-->>CS: provider config
CS->>CS: resolveApiKeyForRequest()
Note right of CS: Priority: codingPlan > pool > legacy
CS->>AKP: getKeyForSession(providerId, sessionId)
AKP-->>CS: resolved API key
CS->>CS: resolveApiFormat(provider)
CS->>Handler: callDirectHandler / callStreamHandler
Handler->>API: HTTP request
API-->>Handler: response / SSE stream
Handler-->>CS: CompletionResult
alt 429/529 error
CS->>AKP: reportError(providerId, sessionId, status)
AKP-->>CS: new key
CS->>Handler: retry with new key
end
CS->>AKP: reportSuccess(sessionId)
CS-->>Frontend: result

Module Boundaries and Responsibilities

ModuleResponsibilitiesNot Responsible For
CompletionServiceRequest pipeline orchestration, format dispatch, retry, vision fallbackProvider CRUD, key storage
ApiKeyPoolServiceMulti-key load balancing, session affinity, cooldown backoffKey persistence (delegated to DB)
LLMConfigServiceProvider config management, model discovery, routing, Transformer chainAPI calls
ProviderManagerProvider CRUD, template/preset system, SQLite persistenceModel discovery, routing config
ModelDiscoveryManagerModel list discovery, caching (SQLite + file)Provider CRUD
ConfigIOManagerConfig import/export, JSON file I/ODatabase operations
AgentModelsManagerRouting config, Transformer management, global parametersProvider CRUD
ThinkingResolvermax_tokens resolution, thinking budget calculationRequest sending
StreamHandlerSSE stream processing (OpenAI/Anthropic/Gemini)Non-streaming requests
DirectApiHandlerNon-streaming API callsStreaming requests
ToolHandlerAgent tool call loopRegular Completion
TransformerHandlerTransformer chain CompletionDirect API calls

IPC Integration Table

IPC ChannelDirectionRouterDescription
llmConfig:getProvidersRenderer -> MainProviderRouterGet all providers
llmConfig:getProviderRenderer -> MainProviderRouterGet a single provider
llmConfig:addProviderRenderer -> MainProviderRouterAdd a provider
llmConfig:updateProviderRenderer -> MainProviderRouterUpdate a provider
llmConfig:deleteProviderRenderer -> MainProviderRouterDelete a provider
llmConfig:toggleProviderRenderer -> MainProviderRouterEnable/disable a provider
llmConfig:discoverModelsRenderer -> MainProviderRouterDiscover model list
llmConfig:getProviderPresetsRenderer -> MainProviderRouterGet preset templates
llmConfig:addFromPresetRenderer -> MainProviderRouterAdd provider from preset
llmConfig:exportConfigRenderer -> MainProviderRouterExport config
llmConfig:importConfigRenderer -> MainProviderRouterImport config
llmConfig:getApiKeysRenderer -> MainApiKeyRouterGet key list
llmConfig:addApiKeyRenderer -> MainApiKeyRouterAdd a key
llmConfig:updateApiKeyRenderer -> MainApiKeyRouterUpdate a key
llmConfig:deleteApiKeyRenderer -> MainApiKeyRouterDelete a key
llmConfig:toggleApiKeyRenderer -> MainApiKeyRouterEnable/disable a key

Extension Points

  • Adding a new API format: Add a URL builder function in url-builder.ts, add handler functions in DirectApiHandler.ts / StreamHandler.ts, and register in the switch in CompletionService
  • Adding a new provider preset: Add a template to the PROVIDER_PRESETS array in provider-presets.ts
  • Adding a new search config: Add an entry to PROVIDER_SEARCH_CONFIGS in provider-presets.ts
  • Custom Transformer: Register a new Transformer chain via AgentModelsManager

FilePurpose
packages/desktop/app/shared/llm-config.tsLLM config shared type definitions
packages/desktop/app/shared/completion-types.tsCompletion type definitions
packages/desktop/app/shared/thinking-config.tsThinking config and budget calculation
packages/desktop/app/shared/provider-presets.tsProvider preset templates and search configs
packages/desktop/app/main/services/routers/llm/schemas.tsIPC request Zod validation schemas
packages/desktop/app/main/services/capabilities/llm/config-service/schemas.tsConfig service internal Zod schemas
packages/desktop/app/main/services/infra/utils/sse-parser.tsSSE stream parsing utilities
packages/desktop/app/main/workers/db/apiKeys.tsAPI key database operations
packages/desktop/app/main/workers/DbClient.tsDatabase client
packages/desktop/app/preload/index.tsPreload API exposure