Skip to content
Open
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
5 changes: 5 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13425,6 +13425,11 @@
<sha256 value="5d5276c1b628afbc42059f340a84defd4bfb5eba18266df3743607e577ed5f87" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin.plugin.serialization" name="org.jetbrains.kotlin.plugin.serialization.gradle.plugin" version="2.3.0">
<artifact name="org.jetbrains.kotlin.plugin.serialization.gradle.plugin-2.3.0.pom">
<sha256 value="d468bd1ab8817fc977783c6944b242a067beeda8dbb175509ed1a5a550ac2a91" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu" version="0.20.2">
<artifact name="atomicfu-0.20.2.module">
<sha256 value="2c995e0e83c583e125706fba3fe85170020d17a88f89d6294a53cd8bd7c4c384" origin="Generated by Gradle"/>
Expand Down
3 changes: 3 additions & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Void>() {
override fun run(client: NextcloudClient): RemoteOperationResult<Void> {
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<Void> {
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<Void>(true, postMethod)
RemoteOperationResult(true, postMethod)
} else {
RemoteOperationResult<Void>(false, postMethod)
RemoteOperationResult(false, postMethod)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* 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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class GetTaskTypesRemoteOperationV2 : OCSRemoteOperation<List<TaskTypeData>>() {
?.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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,20 @@ data class TaskTypeData(
val name: String,
val description: String?,
val inputShape: Map<String, Shape>,
val outputShape: Map<String, Shape>
val outputShape: Map<String, Shape>,
val optionalInputShapeDefaults: Map<String, Any>? = null,
val optionalInputShapeEnumValues: Map<String, List<EnumValue>>? = null,
val inputShapeEnumValues: Map<String, List<EnumValue>>? = null,
val outputShapeEnumValues: Map<String, List<EnumValue>>? = null,
val optionalOutputShapeEnumValues: Map<String, List<EnumValue>>? = 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 =
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* 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<TranslationLanguage>,
val targetLanguages: List<TranslationLanguage>
)

fun TaskTypeData.toTranslationLanguages(): TranslationLanguages {
fun List<EnumValue>?.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
}
Loading