Librechat-Mobile/shared/src/iosMain/kotlin/com/garfiec/librechat/shared/IosSharedModule.kt
Garfie abde86ca4e chore(sync): sync to upstream LibreChat v0.8.5
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.
2026-04-26 23:48:14 -06:00

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(),
)
}
}