Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package io.writeopia

import io.writeopia.api.OllamaApi
import io.writeopia.model.OllamaConfig
import io.writeopia.persistence.OllamaDao
import io.writeopia.requests.ModelsResponse
import io.writeopia.responses.DownloadModelResponse
import io.writeopia.sdk.ai.AiClient
import io.writeopia.sdk.models.utils.ResultData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

private const val SUGGESTION_PROMPT =
"""
Expand Down Expand Up @@ -94,7 +96,7 @@ class OllamaRepository(
override suspend fun getSelectedModel(userId: String): String? =
ollamaDao?.getConfiguration(userId)?.selectedModel

fun listenForConfiguration(id: String) =
fun listenForConfiguration(id: String): StateFlow<OllamaConfig?> =
ollamaDao?.listenForConfiguration(id) ?: MutableStateFlow(null)

suspend fun refreshConfiguration(id: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ class OllamaInjection private constructor(
private val writeopiaDb: WriteopiaDb? = null,
) {

private fun provideOllamaDao(): OllamaDao = OllamaSqlDao(writeopiaDb?.ollamaEntityQueries)
var ollamaDaoInstance: OllamaDao? = null

private fun provideOllamaDao(): OllamaDao = ollamaDaoInstance ?: run {
ollamaDaoInstance = OllamaSqlDao(writeopiaDb?.ollamaEntityQueries)
ollamaDaoInstance!!
}

private fun provideApi() = OllamaApi(
client = appConnectionInjection.provideHttpClient(),
Expand Down
7,596 changes: 3,819 additions & 3,777 deletions application/core/resources/config/ktlint/baseline.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,5 @@
<string name="choose_your_model">Write your AI model</string>
<string name="use_offline">Use in offline mode</string>
<string name="ai_explanation">[Tab - accept; Esc - remove]</string>
<string name="ai_model">AI Model</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,5 @@
<string name="choose_your_model">Escolhe seu modelo de IA</string>
<string name="use_offline">User offline</string>
<string name="ai_explanation">[Tab - aceitar; Esc - remover]</string>
<string name="ai_model">Modelo IA</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,5 @@
<string name="choose_your_model">Write your AI model</string>
<string name="use_offline">Use in offline mode</string>
<string name="ai_explanation">[Tab - accept; Esc - remove]</string>
<string name="ai_model">AI Model</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import writeopia.application.core.resources.generated.resources.account
import writeopia.application.core.resources.generated.resources.action_points
import writeopia.application.core.resources.generated.resources.actions
import writeopia.application.core.resources.generated.resources.ai_explanation
import writeopia.application.core.resources.generated.resources.ai_model
import writeopia.application.core.resources.generated.resources.are_you_sure
import writeopia.application.core.resources.generated.resources.arrangement
import writeopia.application.core.resources.generated.resources.ask_ai
Expand Down Expand Up @@ -407,4 +408,7 @@ object WrStrings {

@Composable
fun aiExplanation() = stringResource(Res.string.ai_explanation)

@Composable
fun aiModel() = stringResource(Res.string.ai_model)
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ fun DesktopNoteEditorScreen(
.align(Alignment.TopEnd),
isDarkTheme,
fontStyleSelected = { noteEditorViewModel.fontFamily },
currentModel = noteEditorViewModel.currentModel,
models = noteEditorViewModel.models,
isEditableState = noteEditorViewModel.isEditable,
isFavorite = noteEditorViewModel.notFavorite,
boldClick = noteEditorViewModel::onAddSpanClick,
Expand Down Expand Up @@ -108,6 +110,7 @@ fun DesktopNoteEditorScreen(
aiActionPoints = noteEditorViewModel::aiActionPoints,
aiFaq = noteEditorViewModel::aiFaq,
aiTags = noteEditorViewModel::aiTags,
selectModel = noteEditorViewModel::selectModel,
)

if (showDeleteConfirmation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
Expand All @@ -44,6 +46,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import io.writeopia.common.utils.collections.inBatches
Expand All @@ -56,12 +59,15 @@ import io.writeopia.resources.WrStrings
import io.writeopia.sdk.models.span.Span
import io.writeopia.theme.WriteopiaTheme
import io.writeopia.ui.icons.WrSdkIcons
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

@Composable
fun SideEditorOptions(
modifier: Modifier = Modifier,
isDarkTheme: Boolean,
currentModel: Flow<String>,
models: Flow<List<String>>,
fontStyleSelected: () -> StateFlow<Font>,
isEditableState: StateFlow<Boolean>,
isFavorite: StateFlow<Boolean>,
Expand All @@ -86,6 +92,7 @@ fun SideEditorOptions(
addPage: () -> Unit,
deleteDocument: () -> Unit,
toggleFavorite: () -> Unit,
selectModel: (String) -> Unit,
) {
var menuType by remember {
mutableStateOf(OptionsType.NONE)
Expand Down Expand Up @@ -155,11 +162,14 @@ fun SideEditorOptions(

OptionsType.AI -> {
AiOptions(
currentModel = currentModel,
models = models,
askAiBySelection = askAiBySelection,
aiSummary = aiSummary,
aiActionPoints = aiActionPoints,
aiFaq = aiFaq,
aiTags = aiTags,
selectModel = selectModel
)
}
}
Expand Down Expand Up @@ -707,6 +717,9 @@ private fun Actions(

@Composable
private fun AiOptions(
currentModel: Flow<String>,
models: Flow<List<String>>,
selectModel: (String) -> Unit,
askAiBySelection: () -> Unit,
aiSummary: () -> Unit,
aiActionPoints: () -> Unit,
Expand Down Expand Up @@ -770,8 +783,51 @@ private fun AiOptions(
onClick = aiTags
)

Spacer(modifier = Modifier.height(8.dp))

val currentModelValue by currentModel.collectAsState(WrStrings.noModelsFound())

Title(WrStrings.aiModel())

var showModels by remember {
mutableStateOf(false)
}

Spacer(modifier = Modifier.height(2.dp))

Text(
text = currentModelValue,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(4.dp).clickable {
showModels = !showModels
}
)

val modelsValue by models.collectAsState(emptyList())

DropdownMenu(
expanded = showModels,
onDismissRequest = { showModels = false },
modifier = Modifier.background(MaterialTheme.colorScheme.background),
offset = DpOffset(y = 6.dp, x = 6.dp)
) {
modelsValue.forEach { model ->
DropdownMenuItem(
text = {
Text(
text = model,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground,
)
},
onClick = {
selectModel(model)
}
)
}
}

Spacer(modifier = Modifier.height(12.dp))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.writeopia.editor.model.EditState
import io.writeopia.model.Font
import io.writeopia.models.interfaces.configuration.WorkspaceConfigRepository
import io.writeopia.repository.UiConfigurationRepository
import io.writeopia.requests.ModelsResponse
import io.writeopia.sdk.export.DocumentToJson
import io.writeopia.sdk.export.DocumentToMarkdown
import io.writeopia.sdk.export.DocumentWriter
Expand Down Expand Up @@ -51,6 +52,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
Expand Down Expand Up @@ -118,6 +120,10 @@ class NoteEditorKmpViewModel(
}
}
}

viewModelScope.launch {
ollamaRepository?.refreshConfiguration(authRepository.getUser().id)
}
}

private var isDarkTheme: Boolean = true
Expand All @@ -133,6 +139,30 @@ class NoteEditorKmpViewModel(
private val _showGlobalMenu = MutableStateFlow(false)
override val showGlobalMenu = _showGlobalMenu.asStateFlow()

@OptIn(ExperimentalCoroutinesApi::class)
private val aiConfigState = authRepository.listenForUser()
.flatMapConcat { user ->
ollamaRepository?.listenForConfiguration(user.id) ?: MutableStateFlow(null)
}

@OptIn(ExperimentalCoroutinesApi::class)
override val currentModel: Flow<String> = aiConfigState.map { config ->
config?.selectedModel ?: "No model selected"
}

@OptIn(ExperimentalCoroutinesApi::class)
override val models: StateFlow<List<String>> = aiConfigState
.filterNotNull()
.flatMapLatest { config ->
ollamaRepository?.listenToModels(config.url)
?: MutableStateFlow(ResultData.Complete(ModelsResponse(emptyList())))
}.map {
when (it) {
is ResultData.Complete -> it.data.models.map { model -> model.model }
else -> emptyList()
}
}.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())

private val _editHeader = MutableStateFlow(false)
override val editHeader = _editHeader.asStateFlow()

Expand Down Expand Up @@ -568,6 +598,17 @@ class NoteEditorKmpViewModel(
}
}

override fun selectModel(model: String) {
if (ollamaRepository != null) {
viewModelScope.launch {
val userId = authRepository.getUser().id

ollamaRepository.saveOllamaSelectedModel(userId, model)
ollamaRepository.refreshConfiguration(userId)
}
}
}

private fun documentPrompt(promptFn: (String, String, String) -> Flow<ResultData<String>>) {
if (ollamaRepository == null) return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.writeopia.ui.backstack.BackstackHandler
import io.writeopia.ui.backstack.BackstackInform
import io.writeopia.ui.manager.WriteopiaStateManager
import io.writeopia.ui.model.DrawState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

interface NoteEditorViewModel : BackstackInform, BackstackHandler {
Expand All @@ -17,6 +18,10 @@ interface NoteEditorViewModel : BackstackInform, BackstackHandler {

val isEditable: StateFlow<Boolean>

val currentModel: Flow<String>

val models: Flow<List<String>>

val showGlobalMenu: StateFlow<Boolean>

val editHeader: StateFlow<Boolean>
Expand Down Expand Up @@ -116,6 +121,8 @@ interface NoteEditorViewModel : BackstackInform, BackstackHandler {
fun receiveExternalFile(files: List<ExternalFile>, position: Int)

fun setTheme(isDarkTheme: Boolean)

fun selectModel(model: String)
}

data class ShareDocument(val content: String, val title: String, val type: String)
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,9 @@ class GlobalShellKmpViewModel(

override fun selectOllamaModel(model: String) {
viewModelScope.launch(Dispatchers.Default) {
ollamaRepository.saveOllamaSelectedModel(getUserId(), model)
val userId = getUserId()
ollamaRepository.saveOllamaSelectedModel(userId, model)
ollamaRepository.refreshConfiguration(userId)
}
}

Expand Down Expand Up @@ -416,10 +418,13 @@ class GlobalShellKmpViewModel(
modelsResult is ResultData.Complete &&
modelsResult.data.models.size == 1
) {
val userId = authRepository.getUser().id

ollamaRepository.saveOllamaSelectedModel(
authRepository.getUser().id,
userId,
modelsResult.data.models.first().model
)
ollamaRepository.refreshConfiguration(userId)
}
}
}
Expand Down