feat: Add audio type according to 2025-03-26 spec (#68)

* feat: Add audio type according to 2025-03-26 spec

* chore: update documentation

* feat: add unit test

* chore: commit api dump

* feat: Add audio type according to 2025-03-26 spec

* chore: update documentation

* feat: add unit test

* chore: rebase and commit api dump

---------

Co-authored-by: Leonid Stashevsky <e5l@users.noreply.github.com>
This commit is contained in:
SeanChinJunKai 2025-04-30 20:58:05 +08:00 • committed by GitHub
commit 9f00f22596
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 119 additions and 29 deletions

View file

@ -897,10 +897,10 @@ public sealed interface PromptMessageContent {
}
/**
* Represents prompt message content that is either text or an image.
* Represents prompt message content that is either text, image or audio.
*/
@Serializable(with = PromptMessageContentTextOrImagePolymorphicSerializer::class)
public sealed interface PromptMessageContentTextOrImage : PromptMessageContent
@Serializable(with = PromptMessageContentMultimodalPolymorphicSerializer::class)
public sealed interface PromptMessageContentMultimodal : PromptMessageContent
/**
* Text provided to or from an LLM.
@ -911,7 +911,7 @@ public data class TextContent(
* The text content of the message.
*/
val text: String? = null,
) : PromptMessageContentTextOrImage {
) : PromptMessageContentMultimodal {
override val type: String = TYPE
public companion object {
@ -933,7 +933,7 @@ public data class ImageContent(
* The MIME type of the image. Different providers may support different image types.
*/
val mimeType: String,
) : PromptMessageContentTextOrImage {
) : PromptMessageContentMultimodal {
override val type: String = TYPE
public companion object {
@ -942,12 +942,35 @@ public data class ImageContent(
}
/**
* An image provided to or from an LLM.
* Audio provided to or from an LLM.
*/
@Serializable
public data class AudioContent(
/**
* The base64-encoded audio data.
*/
val data: String,
/**
* The MIME type of the audio. Different providers may support different audio types.
*/
val mimeType: String,
) : PromptMessageContentMultimodal {
override val type: String = TYPE
public companion object {
public const val TYPE: String = "audio"
}
}
/**
* Unknown content provided to or from an LLM.
*/
@Serializable
public data class UnknownContent(
override val type: String,
) : PromptMessageContentTextOrImage
) : PromptMessageContentMultimodal
/**
* The contents of a resource, embedded into a prompt or tool call result.
@ -1217,7 +1240,7 @@ public class ModelPreferences(
@Serializable
public data class SamplingMessage(
val role: Role,
val content: PromptMessageContentTextOrImage,
val content: PromptMessageContentMultimodal,
)
/**
@ -1299,7 +1322,7 @@ public data class CreateMessageResult(
*/
val stopReason: StopReason? = null,
val role: Role,
val content: PromptMessageContentTextOrImage,
val content: PromptMessageContentMultimodal,
override val _meta: JsonObject = EmptyJsonObject,
) : ClientResult

View file

@ -80,17 +80,19 @@ internal object PromptMessageContentPolymorphicSerializer :
ImageContent.TYPE -> ImageContent.serializer()
TextContent.TYPE -> TextContent.serializer()
EmbeddedResource.TYPE -> EmbeddedResource.serializer()
AudioContent.TYPE -> AudioContent.serializer()
else -> UnknownContent.serializer()
}
}
}
internal object PromptMessageContentTextOrImagePolymorphicSerializer :
JsonContentPolymorphicSerializer<PromptMessageContentTextOrImage>(PromptMessageContentTextOrImage::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<PromptMessageContentTextOrImage> {
internal object PromptMessageContentMultimodalPolymorphicSerializer :
JsonContentPolymorphicSerializer<PromptMessageContentMultimodal>(PromptMessageContentMultimodal::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<PromptMessageContentMultimodal> {
return when (element.jsonObject.getValue("type").jsonPrimitive.content) {
ImageContent.TYPE -> ImageContent.serializer()
TextContent.TYPE -> TextContent.serializer()
AudioContent.TYPE -> AudioContent.serializer()
else -> UnknownContent.serializer()
}
}

View file

@ -0,0 +1,34 @@
package io.modelcontextprotocol.kotlin.sdk
import io.kotest.assertions.json.shouldEqualJson
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
import kotlinx.serialization.encodeToString
import kotlin.test.Test
import kotlin.test.assertEquals
class AudioContentSerializationTest {
private val audioContentJson = """
{
"data": "base64-encoded-audio-data",
"mimeType": "audio/wav",
"type": "audio"
}
""".trimIndent()
private val audioContent = AudioContent(
data = "base64-encoded-audio-data",
mimeType = "audio/wav"
)
@Test
fun `should serialize audio content`() {
McpJson.encodeToString(audioContent) shouldEqualJson audioContentJson
}
@Test
fun `should deserialize audio content`() {
val content = McpJson.decodeFromString<AudioContent>(audioContentJson)
assertEquals(expected = audioContent, actual = content)
}
}