diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b2e472e59f..989cd46925 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -13425,6 +13425,11 @@ + + + + + diff --git a/library/build.gradle b/library/build.gradle index d27ae8ed2c..9497a786a3 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -30,6 +30,7 @@ buildscript { plugins { id "com.diffplug.spotless" version "8.1.0" + id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.0' } apply plugin: 'com.android.library' @@ -59,6 +60,8 @@ configurations { } dependencies { + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0" + implementation 'org.apache.jackrabbit:jackrabbit-webdav:2.13.5' api 'com.squareup.okhttp3:okhttp:5.3.2' implementation 'com.github.bitfireAT:dav4jvm:2.2.1' diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt index eb75f9d751..67921f7695 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt @@ -83,7 +83,9 @@ class AssistantV2Tests : AbstractIT() { val taskType = getTaskType() val selectedTaskType = getSelectedTaskType() - assertTrue(CreateTaskRemoteOperationV2(input, taskType).execute(nextcloudClient).isSuccess) + val createTaskOperation = CreateTaskRemoteOperationV2(input, taskType) + val createTaskOperationResult = createTaskOperation.execute(nextcloudClient) + assertTrue(createTaskOperationResult.isSuccess) var result = GetTaskListRemoteOperationV2(selectedTaskType).execute(nextcloudClient) assertTrue(result.isSuccess) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/CreateTaskRemoteOperationV2.kt b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/CreateTaskRemoteOperationV2.kt index 7974ce4f7e..944cf8df56 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/CreateTaskRemoteOperationV2.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/CreateTaskRemoteOperationV2.kt @@ -13,37 +13,42 @@ import com.nextcloud.operations.PostMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.put import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody import org.apache.commons.httpclient.HttpStatus -class CreateTaskRemoteOperationV2( +open class CreateTaskRemoteOperationV2( private val input: String, private val taskType: TaskTypeData ) : RemoteOperation() { - override fun run(client: NextcloudClient): RemoteOperationResult { + protected open fun buildRequestBody(): String { val inputField = hashMapOf("input" to input) - val requestBody = - hashMapOf( - "input" to inputField, - "type" to taskType.id, - "appId" to "assistant", - "customId" to "" - ) + val jsonObject = + buildJsonObject { + put("input", Json.encodeToJsonElement(inputField)) + put("type", taskType.id) + put("appId", "assistant") + put("customId", "") + } - val json = gson.toJson(requestBody) + return Json.encodeToString(jsonObject) + } + override fun run(client: NextcloudClient): RemoteOperationResult { + val json = buildRequestBody() val request = json.toRequestBody("application/json".toMediaTypeOrNull()) - val postMethod = PostMethod(client.baseUri.toString() + TAG_URL, true, request) - val status = postMethod.execute(client) return if (status == HttpStatus.SC_OK) { - RemoteOperationResult(true, postMethod) + RemoteOperationResult(true, postMethod) } else { - RemoteOperationResult(false, postMethod) + RemoteOperationResult(false, postMethod) } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/CreateTranslationTaskRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/CreateTranslationTaskRemoteOperation.kt new file mode 100644 index 0000000000..9e98a8138e --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/CreateTranslationTaskRemoteOperation.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.assistant.v2 + +import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData +import com.owncloud.android.lib.resources.assistant.v2.model.TranslationRequest +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.put + +class CreateTranslationTaskRemoteOperation( + private val input: TranslationRequest, + private val taskType: TaskTypeData +) : CreateTaskRemoteOperationV2( + input = "", + taskType = taskType + ) { + override fun buildRequestBody(): String { + val jsonObject = + buildJsonObject { + put("input", Json.encodeToJsonElement(input)) + put("type", taskType.id) + put("appId", "assistant") + put("customId", "") + } + + return Json.encodeToString(jsonObject) + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/GetTaskTypesRemoteOperationV2.kt b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/GetTaskTypesRemoteOperationV2.kt index a783f41c98..96a7372f21 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/GetTaskTypesRemoteOperationV2.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/GetTaskTypesRemoteOperationV2.kt @@ -71,7 +71,7 @@ class GetTaskTypesRemoteOperationV2 : OCSRemoteOperation>() { ?.types ?.map { (key, value) -> value.copy(id = value.id ?: key) } ?.filter { taskType -> - isSingleTextInputOutput(taskType) || taskType.isChat() + isSingleTextInputOutput(taskType) || taskType.isChat() || taskType.isTranslate() }?.sortedByDescending { it.isChat() } result = RemoteOperationResult(true, getMethod) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskList.kt b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskList.kt index a33dae69c1..0660dcfe01 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskList.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskList.kt @@ -25,7 +25,9 @@ data class Task( val lastUpdated: Int? = null, val scheduledAt: Int? = null, val endedAt: Int? = null -) +) { + fun isTranslate(): Boolean = (type == "core:text2text:translate") +} data class TaskInput( var input: String? = null diff --git a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskTypes.kt b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskTypes.kt index 6ce58e21e8..20340fe2f5 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskTypes.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/TaskTypes.kt @@ -17,12 +17,20 @@ data class TaskTypeData( val name: String, val description: String?, val inputShape: Map, - val outputShape: Map + val outputShape: Map, + val optionalInputShapeDefaults: Map? = null, + val optionalInputShapeEnumValues: Map>? = null, + val inputShapeEnumValues: Map>? = null, + val outputShapeEnumValues: Map>? = null, + val optionalOutputShapeEnumValues: Map>? = null ) { private val chatTaskName = "Chat" + private val translateTaskName = "Translate" fun isChat(): Boolean = (name == chatTaskName) + fun isTranslate(): Boolean = (name == translateTaskName) + companion object { private const val CONVERSATION_LIST_ID = "ConversationList" val conversationList = @@ -36,6 +44,11 @@ data class TaskTypeData( } } +data class EnumValue( + val name: String, + val value: String +) + data class Shape( val name: String, val description: String, diff --git a/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/Translation.kt b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/Translation.kt new file mode 100644 index 0000000000..ab0ad8c870 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/assistant/v2/model/Translation.kt @@ -0,0 +1,57 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.assistant.v2.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +data class TranslationLanguage( + val name: String, + val code: String +) + +@Serializable +data class TranslationRequest( + @SerialName("origin_language") + val originLanguage: String, + @SerialName("max_tokens") + val maxTokens: Double, + val model: String, + @SerialName("target_language") + val targetLanguage: String, + val input: String +) + +data class TranslationModel( + val model: String, + val maxTokens: Double +) + +data class TranslationLanguages( + val originLanguages: List, + val targetLanguages: List +) + +fun TaskTypeData.toTranslationLanguages(): TranslationLanguages { + fun List?.toTranslationLanguageList() = + this + .orEmpty() + .map { TranslationLanguage(it.name, it.value) } + + return TranslationLanguages( + originLanguages = inputShapeEnumValues?.get("origin_language").toTranslationLanguageList(), + targetLanguages = inputShapeEnumValues?.get("target_language").toTranslationLanguageList() + ) +} + +fun TaskTypeData.toTranslationModel(): TranslationModel? { + val model = optionalInputShapeDefaults?.get("model") as? String + val maxTokens = optionalInputShapeDefaults?.get("max_tokens") as? Double + return if (model != null && maxTokens != null) TranslationModel(model, maxTokens) else null +}