From cd4b352bdf3f811c44e97cd69612d0373877f381 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 03:07:07 +0000 Subject: [PATCH] feat: add typed build parameters and shared DVM tag infrastructure Add shared tag classes (InputTag, OutputTag) and TagArrayBuilder extensions (inputUrl, inputText, inputEvent, inputJob, inputPrompt, output, param) for all DVM kinds to reuse. Update all 17 request event build() methods with typed parameters matching their spec: - 5000: inputUrl, outputMimeType, range, alignment - 5001: eventIds, length, outputMimeType - 5002: eventIds, language - 5050: prompt, model, maxTokens, temperature, topK, topP, frequencyPenalty - 5100: prompt, sourceImageUrl, model, lora, ratio, size, negativePrompt - 5200: videoUrl, outputMimeType, range - 5201: videoUrl, language, subtitle, range, outputFormat - 5202: imageUrl - 5250: text, language - 5302: searchQuery, users, since, until, maxResults - 5303: searchQuery, maxResults - 5400: inputs, relays, groups, filterJson - 5500: fileUrl - 5900: eventId - 5901: text - 5905: eventJson, relays - 5970: eventJson, pow https://claude.ai/code/session_017aamEDeRtQ2H9u5U9B5F1C --- .../NIP90ContentSearchRequestEvent.kt | 12 ++++ .../eventCount/NIP90EventCountRequestEvent.kt | 11 +++- .../NIP90EventPowDelegationRequestEvent.kt | 6 ++ .../NIP90EventPublishScheduleRequestEvent.kt | 8 +++ .../NIP90EventTimestampingRequestEvent.kt | 3 + .../NIP90ImageGenerationRequestEvent.kt | 17 ++++++ .../NIP90ImageToVideoRequestEvent.kt | 3 + .../NIP90MalwareScanRequestEvent.kt | 3 + .../opReturn/NIP90OpReturnRequestEvent.kt | 4 ++ .../NIP90PeopleSearchRequestEvent.kt | 6 ++ .../NIP90SummarizationRequestEvent.kt | 9 +++ .../quartz/nip90Dvms/tags/InputTag.kt | 56 +++++++++++++++++++ .../quartz/nip90Dvms/tags/OutputTag.kt | 41 ++++++++++++++ .../nip90Dvms/tags/TagArrayBuilderExt.kt | 47 ++++++++++++++++ .../quartz/nip90Dvms/tags/TagArrayExt.kt | 27 +++++++++ .../NIP90TextExtractionRequestEvent.kt | 14 +++++ .../NIP90TextGenerationRequestEvent.kt | 19 +++++++ .../NIP90TextToSpeechRequestEvent.kt | 6 ++ .../NIP90TranslationRequestEvent.kt | 6 ++ .../NIP90VideoConversionRequestEvent.kt | 12 ++++ .../NIP90VideoTranslationRequestEvent.kt | 16 ++++++ 21 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/InputTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/OutputTag.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayBuilderExt.kt create mode 100644 quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayExt.kt diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentSearch/NIP90ContentSearchRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentSearch/NIP90ContentSearchRequestEvent.kt index e4c9dbfe66..6ea427fab9 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentSearch/NIP90ContentSearchRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/contentSearch/NIP90ContentSearchRequestEvent.kt @@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +44,20 @@ class NIP90ContentSearchRequestEvent( const val ALT = "NIP90 Content Search request" fun build( + searchQuery: String, + users: String? = null, + since: Long? = null, + until: Long? = null, + maxResults: Int? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputText(searchQuery) + users?.let { param("users", it) } + since?.let { param("since", it.toString()) } + until?.let { param("until", it.toString()) } + maxResults?.let { param("max_results", it.toString()) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventCount/NIP90EventCountRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventCount/NIP90EventCountRequestEvent.kt index 88856680e3..3bbed29c76 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventCount/NIP90EventCountRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventCount/NIP90EventCountRequestEvent.kt @@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +44,17 @@ class NIP90EventCountRequestEvent( const val ALT = "NIP90 Event Count request" fun build( + inputs: List = emptyList(), + relays: List, + groups: List = emptyList(), + filterJson: String = "", createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, - ) = eventTemplate(KIND, "", createdAt) { + ) = eventTemplate(KIND, filterJson, createdAt) { alt(ALT) + inputs.forEach { inputText(it) } + relays.forEach { param("relay", it) } + groups.forEach { param("group", it) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPowDelegation/NIP90EventPowDelegationRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPowDelegation/NIP90EventPowDelegationRequestEvent.kt index fcffe69d0c..3e4842255f 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPowDelegation/NIP90EventPowDelegationRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPowDelegation/NIP90EventPowDelegationRequestEvent.kt @@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +44,14 @@ class NIP90EventPowDelegationRequestEvent( const val ALT = "NIP90 Event PoW Delegation request" fun build( + eventJson: String, + pow: Int, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputText(eventJson) + param("pow", pow.toString()) initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPublishSchedule/NIP90EventPublishScheduleRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPublishSchedule/NIP90EventPublishScheduleRequestEvent.kt index 23a6490fdf..072d6d77b0 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPublishSchedule/NIP90EventPublishScheduleRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventPublishSchedule/NIP90EventPublishScheduleRequestEvent.kt @@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +44,16 @@ class NIP90EventPublishScheduleRequestEvent( const val ALT = "NIP90 Event Publish Schedule request" fun build( + eventJson: String, + relays: List = emptyList(), createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputText(eventJson) + if (relays.isNotEmpty()) { + param("relays", *relays.toTypedArray()) + } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventTimestamping/NIP90EventTimestampingRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventTimestamping/NIP90EventTimestampingRequestEvent.kt index faf65d8621..b48d49aa1c 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventTimestamping/NIP90EventTimestampingRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/eventTimestamping/NIP90EventTimestampingRequestEvent.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputEvent import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +43,12 @@ class NIP90EventTimestampingRequestEvent( const val ALT = "NIP90 Event Timestamping request" fun build( + eventId: HexKey, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputEvent(eventId) initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageGeneration/NIP90ImageGenerationRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageGeneration/NIP90ImageGenerationRequestEvent.kt index 1b5d1240fb..22d4887e24 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageGeneration/NIP90ImageGenerationRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageGeneration/NIP90ImageGenerationRequestEvent.kt @@ -26,6 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText +import com.vitorpamplona.quartz.nip90Dvms.tags.inputUrl +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +45,24 @@ class NIP90ImageGenerationRequestEvent( const val ALT = "NIP90 Image Generation request" fun build( + prompt: String, + sourceImageUrl: String? = null, + model: String? = null, + lora: String? = null, + ratio: String? = null, + size: String? = null, + negativePrompt: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + sourceImageUrl?.let { inputUrl(it) } + inputText(prompt) + model?.let { param("model", it) } + lora?.let { param("lora", it) } + size?.let { param("size", it) } + ratio?.let { param("ratio", it) } + negativePrompt?.let { param("negative_prompt", it) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageToVideo/NIP90ImageToVideoRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageToVideo/NIP90ImageToVideoRequestEvent.kt index f0aa0d025d..597f533022 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageToVideo/NIP90ImageToVideoRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/imageToVideo/NIP90ImageToVideoRequestEvent.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputUrl import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +43,12 @@ class NIP90ImageToVideoRequestEvent( const val ALT = "NIP90 Image-to-Video request" fun build( + imageUrl: String, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputUrl(imageUrl) initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/malwareScanning/NIP90MalwareScanRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/malwareScanning/NIP90MalwareScanRequestEvent.kt index bdf548deb3..9eacb7522f 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/malwareScanning/NIP90MalwareScanRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/malwareScanning/NIP90MalwareScanRequestEvent.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputUrl import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +43,12 @@ class NIP90MalwareScanRequestEvent( const val ALT = "NIP90 Malware Scan request" fun build( + fileUrl: String, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputUrl(fileUrl) initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/opReturn/NIP90OpReturnRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/opReturn/NIP90OpReturnRequestEvent.kt index f148e98679..2482045c81 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/opReturn/NIP90OpReturnRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/opReturn/NIP90OpReturnRequestEvent.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -40,12 +41,15 @@ class NIP90OpReturnRequestEvent( companion object { const val KIND = 5901 const val ALT = "NIP90 OP_RETURN Creation request" + const val MAX_OP_RETURN_BYTES = 80 fun build( + text: String, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputText(text) initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/peopleSearch/NIP90PeopleSearchRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/peopleSearch/NIP90PeopleSearchRequestEvent.kt index 53a638dad3..55e988d5fb 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/peopleSearch/NIP90PeopleSearchRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/peopleSearch/NIP90PeopleSearchRequestEvent.kt @@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +44,14 @@ class NIP90PeopleSearchRequestEvent( const val ALT = "NIP90 People Search request" fun build( + searchQuery: String, + maxResults: Int? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputText(searchQuery) + maxResults?.let { param("max_results", it.toString()) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/summarization/NIP90SummarizationRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/summarization/NIP90SummarizationRequestEvent.kt index 7a7277c0b0..99efffd41e 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/summarization/NIP90SummarizationRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/summarization/NIP90SummarizationRequestEvent.kt @@ -26,6 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputEvent +import com.vitorpamplona.quartz.nip90Dvms.tags.output +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +45,16 @@ class NIP90SummarizationRequestEvent( const val ALT = "NIP90 Summarization request" fun build( + eventIds: List, + length: String? = null, + outputMimeType: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + eventIds.forEach { inputEvent(it) } + length?.let { param("length", it) } + outputMimeType?.let { output(it) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/InputTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/InputTag.kt new file mode 100644 index 0000000000..c745d86962 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/InputTag.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip90Dvms.tags + +import androidx.compose.runtime.Stable +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +@Stable +class InputTag( + val value: String, + val type: String, + val relay: String? = null, + val marker: String? = null, +) { + companion object { + const val TAG_NAME = "i" + + fun parse(tag: Array): InputTag? { + ensure(tag.has(2)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + val relay = if (tag.has(3) && tag[3].isNotBlank()) tag[3] else null + val marker = if (tag.has(4) && tag[4].isNotBlank()) tag[4] else null + return InputTag(tag[1], tag[2], relay, marker) + } + + fun assembleUrl(url: String) = arrayOf(TAG_NAME, url, "url") + + fun assembleText(text: String) = arrayOf(TAG_NAME, text, "text") + + fun assembleEvent(eventId: String) = arrayOf(TAG_NAME, eventId, "event") + + fun assembleJob(jobId: String) = arrayOf(TAG_NAME, jobId, "job") + + fun assemblePrompt(prompt: String) = arrayOf(TAG_NAME, prompt, "prompt") + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/OutputTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/OutputTag.kt new file mode 100644 index 0000000000..84953ae4fc --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/OutputTag.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip90Dvms.tags + +import com.vitorpamplona.quartz.nip01Core.core.has +import com.vitorpamplona.quartz.utils.ensure + +class OutputTag( + val mimeType: String, +) { + companion object { + const val TAG_NAME = "output" + + fun parse(tag: Array): OutputTag? { + ensure(tag.has(1)) { return null } + ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[1].isNotEmpty()) { return null } + return OutputTag(tag[1]) + } + + fun assemble(mimeType: String) = arrayOf(TAG_NAME, mimeType) + } +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayBuilderExt.kt new file mode 100644 index 0000000000..5a86ae6174 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayBuilderExt.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip90Dvms.tags + +import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder + +fun TagArrayBuilder.inputUrl(url: String) = add(InputTag.assembleUrl(url)) + +fun TagArrayBuilder.inputText(text: String) = add(InputTag.assembleText(text)) + +fun TagArrayBuilder.inputEvent(eventId: HexKey) = add(InputTag.assembleEvent(eventId)) + +fun TagArrayBuilder.inputJob(jobId: HexKey) = add(InputTag.assembleJob(jobId)) + +fun TagArrayBuilder.inputPrompt(prompt: String) = add(InputTag.assemblePrompt(prompt)) + +fun TagArrayBuilder.output(mimeType: String) = addUnique(OutputTag.assemble(mimeType)) + +fun TagArrayBuilder.param( + key: String, + value: String, +) = add(arrayOf("param", key, value)) + +fun TagArrayBuilder.param( + key: String, + vararg values: String, +) = add(arrayOf("param", key) + values) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayExt.kt new file mode 100644 index 0000000000..48219735b3 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/tags/TagArrayExt.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.nip90Dvms.tags + +import com.vitorpamplona.quartz.nip01Core.core.TagArray + +fun TagArray.inputs() = mapNotNull(InputTag::parse) + +fun TagArray.outputMimeType() = firstNotNullOfOrNull(OutputTag::parse)?.mimeType diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textExtraction/NIP90TextExtractionRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textExtraction/NIP90TextExtractionRequestEvent.kt index 00b1b69a51..12eb9b8295 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textExtraction/NIP90TextExtractionRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textExtraction/NIP90TextExtractionRequestEvent.kt @@ -26,6 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputUrl +import com.vitorpamplona.quartz.nip90Dvms.tags.output +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +45,21 @@ class NIP90TextExtractionRequestEvent( const val ALT = "NIP90 Text Extraction request" fun build( + inputUrl: String, + outputMimeType: String? = null, + rangeStart: String? = null, + rangeEnd: String? = null, + alignment: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputUrl(inputUrl) + outputMimeType?.let { output(it) } + if (rangeStart != null && rangeEnd != null) { + param("range", rangeStart, rangeEnd) + } + alignment?.let { param("alignment", it) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textGeneration/NIP90TextGenerationRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textGeneration/NIP90TextGenerationRequestEvent.kt index dab1c0d79d..9f56509981 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textGeneration/NIP90TextGenerationRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textGeneration/NIP90TextGenerationRequestEvent.kt @@ -26,6 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputPrompt +import com.vitorpamplona.quartz.nip90Dvms.tags.output +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +45,26 @@ class NIP90TextGenerationRequestEvent( const val ALT = "NIP90 Text Generation request" fun build( + prompt: String, + model: String? = null, + maxTokens: Int? = null, + temperature: Double? = null, + topK: Int? = null, + topP: Double? = null, + frequencyPenalty: Double? = null, + outputMimeType: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputPrompt(prompt) + model?.let { param("model", it) } + maxTokens?.let { param("max_tokens", it.toString()) } + temperature?.let { param("temperature", it.toString()) } + topK?.let { param("top_k", it.toString()) } + topP?.let { param("top_p", it.toString()) } + frequencyPenalty?.let { param("frequency_penalty", it.toString()) } + outputMimeType?.let { output(it) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textToSpeech/NIP90TextToSpeechRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textToSpeech/NIP90TextToSpeechRequestEvent.kt index 5fe03ec104..ee842cb4cf 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textToSpeech/NIP90TextToSpeechRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/textToSpeech/NIP90TextToSpeechRequestEvent.kt @@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputText +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +44,14 @@ class NIP90TextToSpeechRequestEvent( const val ALT = "NIP90 Text-to-Speech request" fun build( + text: String, + language: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputText(text) + language?.let { param("language", it) } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/translation/NIP90TranslationRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/translation/NIP90TranslationRequestEvent.kt index a8327dc000..167060cebf 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/translation/NIP90TranslationRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/translation/NIP90TranslationRequestEvent.kt @@ -26,6 +26,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputEvent +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +44,14 @@ class NIP90TranslationRequestEvent( const val ALT = "NIP90 Translation request" fun build( + eventIds: List, + language: String, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + eventIds.forEach { inputEvent(it) } + param("language", language) initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoConversion/NIP90VideoConversionRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoConversion/NIP90VideoConversionRequestEvent.kt index c1da0736b9..17efb7342a 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoConversion/NIP90VideoConversionRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoConversion/NIP90VideoConversionRequestEvent.kt @@ -26,6 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputUrl +import com.vitorpamplona.quartz.nip90Dvms.tags.output +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +45,19 @@ class NIP90VideoConversionRequestEvent( const val ALT = "NIP90 Video Conversion request" fun build( + videoUrl: String, + outputMimeType: String? = null, + rangeStart: String? = null, + rangeEnd: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputUrl(videoUrl) + outputMimeType?.let { output(it) } + if (rangeStart != null && rangeEnd != null) { + param("range", rangeStart, rangeEnd) + } initializer() } } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoTranslation/NIP90VideoTranslationRequestEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoTranslation/NIP90VideoTranslationRequestEvent.kt index 019f0606c5..d1fcbd0c6d 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoTranslation/NIP90VideoTranslationRequestEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip90Dvms/videoTranslation/NIP90VideoTranslationRequestEvent.kt @@ -26,6 +26,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip31Alts.alt +import com.vitorpamplona.quartz.nip90Dvms.tags.inputUrl +import com.vitorpamplona.quartz.nip90Dvms.tags.output +import com.vitorpamplona.quartz.nip90Dvms.tags.param import com.vitorpamplona.quartz.utils.TimeUtils @Immutable @@ -42,10 +45,23 @@ class NIP90VideoTranslationRequestEvent( const val ALT = "NIP90 Video Translation request" fun build( + videoUrl: String, + language: String, + subtitle: String? = null, + rangeStart: String? = null, + rangeEnd: String? = null, + outputFormat: String? = null, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, ) = eventTemplate(KIND, "", createdAt) { alt(ALT) + inputUrl(videoUrl) + param("language", language) + subtitle?.let { param("subtitle", it) } + if (rangeStart != null && rangeEnd != null) { + param("range", rangeStart, rangeEnd) + } + outputFormat?.let { output(it) } initializer() } }