Targets upstream tag v0.8.5 (commit 9ccc8d9be). Bumps the supported
backend version constant, advances the upstream submodule pin, and
adds wire-protocol parity for new server features.
Wire protocol
- Add ContentType.SUMMARY enum value and flat sibling fields on
MessageContentPart (content as JsonElement to absorb array/string/
legacy-text variants per BaseClient.getSummaryText).
- Add Anthropic effort.xhigh dropdown value, ThinkingDisplay (Opus 4.7
reasoning visibility), and Message.contextMeta pass-through.
- Add allowAccountDeletion to StartupConfig (defaults to true to
preserve older-server behavior).
- Drop unused instanceProjectId from StartupConfig and from
ConfigRepositoryImpl.isValidLibreChatConfig.
New endpoints / features
- Favorites: UserFavorite DTO + FavoritesApi + FavoritesRepository,
Settings -> Favorites sub-screen (list/unpin), pin icons in chat
model selector. Repository owns the canonical list as a StateFlow
guarded by Mutex.withLock so chat-side toggles and settings-side
bulk-unpin can't drift apart and concurrent writes can't lose each
other.
- Prompt-group usage telemetry (POST /api/prompts/groups/:id/use,
fire-and-forget on prompt insertion).
- Summarize SSE events (on_summarize_complete -> StreamEvent.ContextSummary)
and a collapsible "Summarized earlier messages" card rendered from
the persisted SUMMARY content part. Forward-compat else-arm preserved
on both LangGraph and legacy SSE mappers.
Backward compatibility
- BackendVersion.isCompatibleOrNewer helper added; documented fail-open
contract for callers that have already null-checked
detectedBackendVersion.
- New VERSION_GATES.md catalog at repo root tracks every code path that
branches on server version. Initial entries:
- isCollaborative agent toggle (hidden on v0.8.5+; full-ACL UX deferred)
- xhigh effort dropdown value (filtered out on <v0.8.5 to avoid
server-side enum rejection)
Defensive hardening
- ChatApi.startChat asserts Content-Type: application/json before
decoding ChatStartResponse and catches NoTransformationFoundException
as a backstop, translating both to a friendly ApiException. Observed
during testing on v0.8.5-rc1 with summarization enabled, where the
agents chat-start endpoint returned 200 OK with no Content-Type and
Ktor leaked a NoTransformationFoundException stack trace into the
chat surface. Belt-and-suspenders against future backend regressions
or unsupported-server pointing.
Documentation
- DISCOVERY.md updated for the v0.8.5 surface: /api/endpoints now
requires JWT, /api/config pre-auth/post-auth split, allowAccountDeletion,
instanceProjectId removal, favorites endpoints (with all three XOR
variants: agentId | model+endpoint | spec), prompt-usage endpoint,
/api/admin/** marked out-of-scope. Plus a new MessageContentPart +
SUMMARY wire-shape section.
iOS
- Swift SharedFrameworkTest.swift updated for the new
StreamEvent.ContextSummary variant and Message.contextMeta
constructor argument.
Out of scope (by design)
- /api/admin/** route family - web-only feature.
- Full ACL migration for agent sharing - replaced by version-gating
the legacy isCollaborative toggle.
- Spec-pinning UI on mobile - UserFavorite.spec round-trips for
fidelity with web-authored pins, but mobile does not yet surface
a spec picker.
144 lines
5.1 KiB
Kotlin
144 lines
5.1 KiB
Kotlin
package com.garfiec.librechat.shared
|
|
|
|
import com.garfiec.librechat.core.common.di.KoinQualifiers
|
|
import com.garfiec.librechat.core.common.di.commonModule
|
|
import com.garfiec.librechat.core.data.di.dataModule
|
|
import com.garfiec.librechat.core.network.api.AgentsApi
|
|
import com.garfiec.librechat.core.network.api.ApiKeysApi
|
|
import com.garfiec.librechat.core.network.api.AuthApi
|
|
import com.garfiec.librechat.core.network.api.BalanceApi
|
|
import com.garfiec.librechat.core.network.api.BannerApi
|
|
import com.garfiec.librechat.core.network.api.ChatApi
|
|
import com.garfiec.librechat.core.network.api.ConfigApi
|
|
import com.garfiec.librechat.core.network.api.ConversationsApi
|
|
import com.garfiec.librechat.core.network.api.FavoritesApi
|
|
import com.garfiec.librechat.core.network.api.FilesApi
|
|
import com.garfiec.librechat.core.network.api.FilesExtApi
|
|
import com.garfiec.librechat.core.network.api.KeysApi
|
|
import com.garfiec.librechat.core.network.api.McpApi
|
|
import com.garfiec.librechat.core.network.api.MemoriesApi
|
|
import com.garfiec.librechat.core.network.api.MessagesApi
|
|
import com.garfiec.librechat.core.network.api.PresetsApi
|
|
import com.garfiec.librechat.core.network.api.PromptsApi
|
|
import com.garfiec.librechat.core.network.api.RolesApi
|
|
import com.garfiec.librechat.core.network.api.SearchApi
|
|
import com.garfiec.librechat.core.network.api.ShareApi
|
|
import com.garfiec.librechat.core.network.api.SpeechApi
|
|
import com.garfiec.librechat.core.network.api.TagsApi
|
|
import com.garfiec.librechat.core.network.api.UserApi
|
|
import com.garfiec.librechat.core.network.client.LibreChatHttpClient
|
|
import com.garfiec.librechat.core.network.client.ServerUrlProvider
|
|
import com.garfiec.librechat.core.network.client.TokenManager
|
|
import com.garfiec.librechat.core.network.sse.SseClient
|
|
import com.garfiec.librechat.core.network.sse.SseHttpTransport
|
|
import com.garfiec.librechat.feature.agents.di.agentsModule
|
|
import com.garfiec.librechat.feature.auth.di.authModule
|
|
import com.garfiec.librechat.feature.chat.di.chatModule
|
|
import com.garfiec.librechat.feature.conversations.di.conversationsModule
|
|
import com.garfiec.librechat.feature.files.di.filesModule
|
|
import com.garfiec.librechat.feature.settings.di.settingsModule
|
|
import io.ktor.client.HttpClient
|
|
import io.ktor.client.engine.darwin.Darwin
|
|
import io.ktor.client.plugins.HttpTimeout
|
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
|
import io.ktor.client.plugins.defaultRequest
|
|
import io.ktor.http.ContentType
|
|
import io.ktor.http.contentType
|
|
import io.ktor.http.takeFrom
|
|
import io.ktor.serialization.kotlinx.json.json
|
|
import kotlinx.serialization.json.Json
|
|
import org.koin.core.module.dsl.singleOf
|
|
import org.koin.dsl.module
|
|
|
|
/**
|
|
* iOS-specific Koin module providing platform implementations and HTTP clients.
|
|
* Includes dataModule which provides repositories, DAOs, DataStore, and token storage.
|
|
*/
|
|
val iosSharedModule = module {
|
|
|
|
includes(commonModule)
|
|
includes(dataModule)
|
|
includes(authModule)
|
|
includes(chatModule)
|
|
includes(conversationsModule)
|
|
includes(settingsModule)
|
|
includes(agentsModule)
|
|
includes(filesModule)
|
|
|
|
single {
|
|
Json {
|
|
ignoreUnknownKeys = true
|
|
isLenient = true
|
|
encodeDefaults = false
|
|
explicitNulls = false
|
|
coerceInputValues = true
|
|
}
|
|
}
|
|
|
|
// Main HttpClient (with auth interceptor)
|
|
single {
|
|
LibreChatHttpClient.create(
|
|
engineFactory = Darwin,
|
|
json = get(),
|
|
tokenManager = get(),
|
|
serverUrlProvider = get(),
|
|
)
|
|
}
|
|
|
|
// Refresh HttpClient (no auth interceptor, short timeout)
|
|
single(KoinQualifiers.Refresh) {
|
|
val json = get<Json>()
|
|
val serverUrlProvider = get<ServerUrlProvider>()
|
|
HttpClient(Darwin) {
|
|
install(ContentNegotiation) { json(json) }
|
|
install(HttpTimeout) {
|
|
requestTimeoutMillis = 15_000
|
|
connectTimeoutMillis = 10_000
|
|
}
|
|
defaultRequest {
|
|
val baseUrl = serverUrlProvider.getBaseUrl()
|
|
if (baseUrl.isNotEmpty()) {
|
|
url.takeFrom(baseUrl)
|
|
}
|
|
contentType(ContentType.Application.Json)
|
|
}
|
|
}
|
|
}
|
|
|
|
// API services (all take HttpClient as constructor parameter)
|
|
singleOf(::AuthApi)
|
|
singleOf(::ChatApi)
|
|
singleOf(::ConfigApi)
|
|
singleOf(::ConversationsApi)
|
|
singleOf(::FilesApi)
|
|
singleOf(::FilesExtApi)
|
|
singleOf(::MessagesApi)
|
|
singleOf(::UserApi)
|
|
singleOf(::AgentsApi)
|
|
singleOf(::FavoritesApi)
|
|
singleOf(::PresetsApi)
|
|
singleOf(::PromptsApi)
|
|
singleOf(::RolesApi)
|
|
singleOf(::TagsApi)
|
|
singleOf(::ShareApi)
|
|
singleOf(::SearchApi)
|
|
singleOf(::BalanceApi)
|
|
singleOf(::BannerApi)
|
|
singleOf(::KeysApi)
|
|
singleOf(::ApiKeysApi)
|
|
singleOf(::McpApi)
|
|
singleOf(::MemoriesApi)
|
|
singleOf(::SpeechApi)
|
|
single { SseHttpTransport(get(), get()) }
|
|
singleOf(::SseClient)
|
|
|
|
// SDK facade
|
|
single {
|
|
LibreChatSDK(
|
|
authApi = get(),
|
|
chatApi = get(),
|
|
sseClient = get(),
|
|
tokenManager = get(),
|
|
)
|
|
}
|
|
}
|