3.1 KiB
3.1 KiB
core:network
Ktor HttpClient, API service classes, SSE streaming client, auth interceptor. All HTTP communication lives here.
What This Module Provides
- Ktor HttpClient factory (
di/NetworkModule.kt): Provides singletonHttpClient(OkHttp)with ContentNegotiation, Logging, HttpTimeout, HttpRequestRetry, and AuthInterceptorPlugin via Koin module. - AuthInterceptorPlugin (
client/AuthInterceptor.kt): Custom Ktor plugin that injectsAuthorization: Beareron outgoing requests and retries on 401 after refreshing tokens. Skips auth endpoints (auth/login,auth/register,auth/refresh, etc.). - TokenManager interface (
client/TokenManager.kt):getAccessToken(),setTokens(),refreshAccessToken()(Mutex-guarded),clearTokens(),sessionExpiredFlow. Implemented in:core:data. - ServerUrlProvider interface (
client/ServerUrlProvider.kt): Resolves the user-configured base URL. Implemented in:core:data. - API services (
api/): One class per domain --AuthApi,ConversationsApi,MessagesApi,ChatStreamApi,FilesApi,AgentsApi,PresetsApi,PromptsApi,TagsApi,ShareApi,ConfigApi,EndpointsApi,BalanceApi,UserApi,SearchApi. Each takesHttpClientas a constructor parameter, wired via Koin. - SSE client (
sse/):SseClient,SseEvent,SseEventParser,SseConnectionManager. - DTO mappers (
mapper/): Convert network DTOs to domain models from:core:model.
SSE Streaming Architecture
Do NOT use Ktor's SSE plugin. Use the custom line parser over raw ByteReadChannel.
Two-phase protocol:
POST /api/agents/chatwith full message payload -> returns{ streamId }(streamId === conversationId)GET /api/agents/chat/stream/:streamIdopens the SSE event stream
Legacy single-phase path (OpenAI Assistants): POST body is the SSE stream itself. Both paths need the custom parser.
SseConnectionManager handles lifecycle: start, reconnect with exponential backoff (1s/2s/4s/8s, max 5 retries), abort via POST /api/agents/chat/abort, and exposes StateFlow<StreamingState>.
On reconnection, append ?resume=true to get a sync event with runSteps[] + aggregatedContent[].
Key Configuration
Json { ignoreUnknownKeys = true; isLenient = true; encodeDefaults = false; explicitNulls = false; coerceInputValues = true }socketTimeoutMillis = 120_000(SSE streams override toLong.MAX_VALUE)- Browser-like
User-Agentheader required -- backendua-parser-jsmiddleware may reject non-browser UAs.
Error Handling
HttpResponseValidatorin the client converts non-2xx to exceptions.- API services throw on error. Repositories in
:core:datacatch viasafeApiCallfrom:core:common. - Respect
429 Too Many Requestsand parseRetry-Afterheader on auth endpoints.
Rules
- Dependencies:
:core:model,:core:common, Ktor bundles, kotlinx-serialization, Timber, Koin. - Convention plugins:
librechat.mobile.library+librechat.mobile.koin+librechat.kotlin.serialization. - API services must not contain business logic -- they are thin HTTP wrappers.
- All
arg-wrapped endpoints must match the backend pattern:setBody(mapOf("arg" to mapOf(...))).