…lines 1–4 unchanged (4 lines)
5package mozilla.components.lib.llm.mlpa
6
7import kotlinx.coroutines.flow.MutableStateFlow
8import kotlinx.coroutines.flow.StateFlow
9import mozilla.components.concept.llm.CloudLlmProvider
10import mozilla.components.concept.llm.CloudLlmProvider.State
11import mozilla.components.concept.llm.LlmProvider
12import mozilla.components.lib.llm.mlpa.service.ChatService
13import mozilla.components.lib.llm.mlpa.service.ChatServiceError
14import mozilla.components.lib.llm.mlpa.service.ChatServiceException
15import mozilla.components.lib.llm.mlpa.service.MlpaService
16
17/**
18 * [CloudLlmProvider] implementation backed by MLPA services.
19 *
20 * This provider is responsible for:
…lines 21–28 unchanged (8 lines)
29 *
30 * @property tokenProvider Responsible for fetching the MLPA authentication token.
31 * @property mlpaService Service used to construct the [MlpaLlm] instance once authenticated.
32 */
33class MlpaLlmProvider(
34 val tokenProvider: MlpaTokenProvider,
35 val storage: MlpaTokenStorage,
36 val mlpaService: MlpaService,
37) : CloudLlmProvider {
38 override val info = LlmProvider.Info(nameRes = R.string.mlpa_llm_provider_name, iconRes = R.drawable.firefox_icon)
39 private val _state = MutableStateFlow<State>(State.Available)
40
41 /**
42 * The current state.
43 */
44 override val state: StateFlow<State> = _state
45
46 /**
47 * Prepares the provider for use.
48 *
49 * This function attempts to fetch an authentication token using [tokenProvider].
50 *
51 * - On success, updates [state] to [State.Ready] with a newly created [MlpaLlm].
52 * - On failure, updates [state] to [State.Unavailable].
53 */
54 override suspend fun prepare() {
55 tokenProvider.fetchToken()
56 .onSuccess { _state.value = State.Ready(MlpaLlm(chatService, it)) }
57 .onFailure { _state.value = State.Unavailable }
58 }
59
60 /**
61 * Wraps the [ChatService]
62 */
63 private val chatService = ChatService { token, request ->
64 val response = mlpaService.completion(token, request)
65 response.onFailure {
66 if (it.isRetryable) {
67 storage.clear()
68 _state.value = State.Available
69 } else {
70 _state.value = State.Unavailable
71 }
72 }
73 return@ChatService response
74 }
75}
76
77private val Throwable.isRetryable get() = (this as? ChatServiceException)?.error is ChatServiceError.InvalidToken
78