Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
Expand Up @@ -58,10 +58,10 @@ private constructor(
/**
* Duration of the input audio in seconds.
*
* @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
* @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
fun duration(): Double = duration.getRequired("duration")
fun duration(): Optional<Double> = duration.getOptional("duration")
Comment thread
wskr00 marked this conversation as resolved.
Outdated

/**
* Segments of the transcript annotated with timestamps and speaker labels.
Expand Down Expand Up @@ -149,7 +149,6 @@ private constructor(
*
* The following fields are required:
* ```java
* .duration()
* .segments()
* .text()
* ```
Expand All @@ -160,7 +159,7 @@ private constructor(
/** A builder for [TranscriptionDiarized]. */
class Builder internal constructor() {

private var duration: JsonField<Double>? = null
private var duration: JsonField<Double> = JsonMissing.of()
private var segments: JsonField<MutableList<TranscriptionDiarizedSegment>>? = null
private var task: JsonValue = JsonValue.from("transcribe")
private var text: JsonField<String>? = null
Expand All @@ -178,7 +177,17 @@ private constructor(
}

/** Duration of the input audio in seconds. */
fun duration(duration: Double) = duration(JsonField.of(duration))
fun duration(duration: Double?) = duration(JsonField.ofNullable(duration))

/**
* Alias for [Builder.duration].
*
* This unboxed primitive overload exists for backwards compatibility.
*/
fun duration(duration: Double) = duration(duration as Double?)

/** Alias for calling [Builder.duration] with `duration.orElse(null)`. */
fun duration(duration: Optional<Double>) = duration(duration.getOrNull())

/**
* Sets [Builder.duration] to an arbitrary JSON value.
Expand Down Expand Up @@ -294,7 +303,6 @@ private constructor(
*
* The following fields are required:
* ```java
* .duration()
* .segments()
* .text()
* ```
Expand All @@ -303,7 +311,7 @@ private constructor(
*/
fun build(): TranscriptionDiarized =
TranscriptionDiarized(
checkRequired("duration", duration),
duration,
checkRequired("segments", segments).map { it.toImmutable() },
task,
checkRequired("text", text),
Expand All @@ -330,7 +338,7 @@ private constructor(
duration()
segments().forEach { it.validate() }
Comment thread
wskr00 marked this conversation as resolved.
_task().let {
if (it != JsonValue.from("transcribe")) {
if (it != JsonMissing.of() && it != JsonValue.from("transcribe")) {
Comment thread
wskr00 marked this conversation as resolved.
Outdated
throw OpenAIInvalidDataException("'task' is invalid, received $it")
}
}
Comment on lines 352 to 356

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m intentionally not scoring missing or null task values in validity(). They remain accepted by validate(), but treating an absent field as positive diarized evidence causes plain responses such as {"text":"hello"} to outrank Transcription. This regression is covered by a test.

Expand All @@ -355,6 +363,7 @@ private constructor(
@JvmSynthetic
internal fun validity(): Int =
(if (duration.asKnown().isPresent) 1 else 0) +
(if (segments.asKnown().isPresent) 1 else 0) +
Comment thread
wskr00 marked this conversation as resolved.
(segments.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
task.let { if (it == JsonValue.from("transcribe")) 1 else 0 } +
(if (text.asKnown().isPresent) 1 else 0) +
Comment thread
wskr00 marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,24 @@ internal class TranscriptionCreateResponseTest {
assertThat(roundtrippedTranscriptionCreateResponse).isEqualTo(transcriptionCreateResponse)
}

@Test
fun deserializeDiarizedWithEmptySegments() {
val transcriptionCreateResponse =
jsonMapper()
.readValue(
"""
{
"text": "",
"segments": []
}
"""
.trimIndent(),
jacksonTypeRef<TranscriptionCreateResponse>(),
)

assertThat(transcriptionCreateResponse.isDiarized()).isTrue()
}

@Test
fun ofVerbose() {
val verbose =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package com.openai.models.audio.transcriptions

import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import com.openai.core.JsonValue
import com.openai.core.jsonMapper
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
Expand All @@ -13,7 +14,6 @@ internal class TranscriptionDiarizedTest {
fun create() {
val transcriptionDiarized =
TranscriptionDiarized.builder()
.duration(0.0)
.addSegment(
TranscriptionDiarizedSegment.builder()
.id("id")
Expand All @@ -39,7 +39,7 @@ internal class TranscriptionDiarizedTest {
)
.build()

assertThat(transcriptionDiarized.duration()).isEqualTo(0.0)
assertThat(transcriptionDiarized.duration()).isEmpty
assertThat(transcriptionDiarized.segments())
.containsExactly(
TranscriptionDiarizedSegment.builder()
Expand Down Expand Up @@ -69,6 +69,82 @@ internal class TranscriptionDiarizedTest {
)
}

@Test
fun deserializeWithoutOptionalFields() {
val transcriptionDiarized =
jsonMapper()
.readValue(
"""
{
"text": "text",
"segments": [
{
"type": "transcript.text.segment",
"id": "id",
"end": 1.0,
"speaker": "speaker",
"start": 0.0,
"text": "text"
}
]
}
"""
.trimIndent(),
jacksonTypeRef<TranscriptionDiarized>(),
)

assertThat(transcriptionDiarized.duration()).isEmpty
assertThat(transcriptionDiarized.isValid()).isTrue()
}

@Test
fun rejectsInvalidDuration() {
val transcriptionDiarized =
jsonMapper()
.readValue(
"""
{
"duration": "invalid",
"text": "text",
"segments": [
{
"type": "transcript.text.segment",
"id": "id",
"end": 1.0,
"speaker": "speaker",
"start": 0.0,
"text": "text"
}
]
}
"""
.trimIndent(),
jacksonTypeRef<TranscriptionDiarized>(),
)

assertThat(transcriptionDiarized.isValid()).isFalse()
}

@Test
fun rejectsUnexpectedTask() {
val transcriptionDiarized =
TranscriptionDiarized.builder()
.addSegment(
TranscriptionDiarizedSegment.builder()
.id("id")
.end(1.0)
.speaker("speaker")
.start(0.0)
.text("text")
.build()
)
.task(JsonValue.from("translate"))
.text("text")
.build()

assertThat(transcriptionDiarized.isValid()).isFalse()
}

@Test
fun roundtrip() {
val jsonMapper = jsonMapper()
Expand Down Expand Up @@ -107,5 +183,6 @@ internal class TranscriptionDiarizedTest {
)

assertThat(roundtrippedTranscriptionDiarized).isEqualTo(transcriptionDiarized)
assertThat(roundtrippedTranscriptionDiarized.duration()).contains(0.0)
}
}