Skip to content

Commit d6610fa

Browse files
authored
Merge branch 'main' into main
2 parents fe3b800 + 445160e commit d6610fa

File tree

12 files changed

+101
-10
lines changed

12 files changed

+101
-10
lines changed

.tool-versions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
java temurin-11.0.22+7

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## Unreleased
2+
3+
### Added
4+
5+
- **Audio**: add `timestampGranularities` (thanks @mxwell)
6+
7+
### Fixed
8+
9+
- **Core**: nullable `OpenAIErrorDetails` fields (#315)
10+
- **Messages**: nullable field `MessageContent.Image#fileId` (#313)
11+
112
## 3.7.0
213

314
### Added

openai-client/src/commonMain/kotlin/com.aallam.openai.client/internal/api/AudioApi.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ internal class AudioApi(val requester: HttpRequester) : Audio {
6161
request.responseFormat?.let { append(key = "response_format", value = it.value) }
6262
request.temperature?.let { append(key = "temperature", value = it) }
6363
request.language?.let { append(key = "language", value = it) }
64+
if (request.responseFormat == AudioResponseFormat.VerboseJson) {
65+
for (timestampGranularity in request.timestampGranularities.orEmpty()) {
66+
append(key = "timestamp_granularities[]", value = timestampGranularity.value)
67+
}
68+
}
6469
}
6570

6671
@BetaOpenAI

openai-client/src/commonTest/kotlin/com/aallam/openai/client/TestAudio.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ class TestAudio : TestOpenAI() {
5555
assertTrue { transcription.segments?.isNotEmpty() ?: false }
5656
}
5757

58+
@Test
59+
fun transcriptionWithWordTimestamps() = test {
60+
val request = transcriptionRequest {
61+
audio = FileSource(path = testFilePath("audio/micro-machines.wav"), fileSystem = TestFileSystem)
62+
model = ModelId("whisper-1")
63+
responseFormat = AudioResponseFormat.VerboseJson
64+
timestampGranularities = listOf(TimestampGranularity.Word)
65+
}
66+
val transcription = openAI.transcription(request)
67+
assertTrue { transcription.text.isNotEmpty() }
68+
assertEquals(transcription.language, "english")
69+
assertEquals(transcription.duration!!, 29.88, absoluteTolerance = 0.1)
70+
assertEquals(transcription.segments, null)
71+
assertTrue { transcription.words?.isNotEmpty() ?: false }
72+
}
73+
5874
@Test
5975
fun translation() = test {
6076
val request = translationRequest {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.aallam.openai.api.audio
2+
3+
import kotlinx.serialization.Serializable
4+
import kotlin.jvm.JvmInline
5+
6+
@Serializable
7+
@JvmInline
8+
public value class TimestampGranularity(public val value: String) {
9+
public companion object {
10+
public val Word: TimestampGranularity = TimestampGranularity("word")
11+
public val Segment: TimestampGranularity = TimestampGranularity("segment")
12+
}
13+
}

openai-core/src/commonMain/kotlin/com.aallam.openai.api/audio/Transcription.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ public data class Transcription(
2020
@SerialName("language") val language: String? = null,
2121
@SerialName("duration") val duration: Double? = null,
2222
@SerialName("segments") val segments: List<Segment>? = null,
23+
@SerialName("words") val words: List<Word>? = null,
2324
)

openai-core/src/commonMain/kotlin/com.aallam.openai.api/audio/TranscriptionRequest.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.aallam.openai.api.audio
22

3-
import com.aallam.openai.api.BetaOpenAI
43
import com.aallam.openai.api.OpenAIDsl
54
import com.aallam.openai.api.file.FileSource
65
import com.aallam.openai.api.model.ModelId
@@ -43,6 +42,14 @@ public class TranscriptionRequest(
4342
* [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) format will improve accuracy and latency.
4443
*/
4544
public val language: String? = null,
45+
46+
/**
47+
* The timestamp granularities to populate for this transcription.
48+
* [responseFormat] must be set [AudioResponseFormat.VerboseJson] to use timestamp granularities.
49+
* Either or both of these options are supported: [TimestampGranularity.Word], or [TimestampGranularity.Segment].
50+
* Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
51+
*/
52+
public val timestampGranularities: List<TimestampGranularity>? = null,
4653
)
4754

4855
/**
@@ -90,6 +97,14 @@ public class TranscriptionRequestBuilder {
9097
*/
9198
public var language: String? = null
9299

100+
/**
101+
* The timestamp granularities to populate for this transcription.
102+
* responseFormat must be set verbose_json to use timestamp granularities.
103+
* Either or both of these options are supported: word, or segment.
104+
* Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
105+
*/
106+
public var timestampGranularities: List<TimestampGranularity>? = null
107+
93108
/**
94109
* Builder of [TranscriptionRequest] instances.
95110
*/
@@ -100,5 +115,6 @@ public class TranscriptionRequestBuilder {
100115
responseFormat = responseFormat,
101116
temperature = temperature,
102117
language = language,
118+
timestampGranularities = timestampGranularities,
103119
)
104120
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.aallam.openai.api.audio
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
public data class Word(
8+
@SerialName("word") val word: String,
9+
@SerialName("start") val start: Double,
10+
@SerialName("end") val end: Double,
11+
)

openai-core/src/commonMain/kotlin/com.aallam.openai.api/exception/OpenAIErrorDetails.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import kotlinx.serialization.Serializable
1010
*/
1111
@Serializable
1212
public data class OpenAIError(
13-
@SerialName("error") public val detail: OpenAIErrorDetails?,
13+
@SerialName("error") public val detail: OpenAIErrorDetails? = null,
1414
)
1515

1616
/**
@@ -23,8 +23,8 @@ public data class OpenAIError(
2323
*/
2424
@Serializable
2525
public data class OpenAIErrorDetails(
26-
@SerialName("code") val code: String?,
27-
@SerialName("message") val message: String?,
28-
@SerialName("param") val param: String?,
29-
@SerialName("type") val type: String?,
26+
@SerialName("code") val code: String? = null,
27+
@SerialName("message") val message: String? = null,
28+
@SerialName("param") val param: String? = null,
29+
@SerialName("type") val type: String? = null,
3030
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.aallam.openai.api.message
2+
3+
import com.aallam.openai.api.file.FileId
4+
import kotlinx.serialization.SerialName
5+
import kotlinx.serialization.Serializable
6+
7+
/**
8+
* References an image File in the content of a message.
9+
*/
10+
@Serializable
11+
public data class ImageFile(
12+
/**
13+
* The File ID of the image in the message content.
14+
*/
15+
@SerialName("file_id") val fileId: FileId
16+
)

openai-core/src/commonMain/kotlin/com.aallam.openai.api/message/MessageContent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public sealed interface MessageContent {
3535
/**
3636
* The File ID of the image in the message content.
3737
*/
38-
@SerialName("file_id") val fileId: FileId
38+
@SerialName("image_file") val imageFile: ImageFile
3939
) : MessageContent
4040

4141
}

openai-core/src/commonMain/kotlin/com.aallam.openai.api/message/MessageFile.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.aallam.openai.api.message
22

33
import com.aallam.openai.api.BetaOpenAI
44
import com.aallam.openai.api.file.FileId
5+
import kotlinx.serialization.SerialName
56
import kotlinx.serialization.Serializable
67

78
/**
@@ -13,13 +14,13 @@ public data class MessageFile(
1314
/**
1415
* The identifier, which can be referenced in API endpoints.
1516
*/
16-
val id: FileId,
17+
@SerialName("id") val id: FileId,
1718
/**
1819
* The Unix timestamp (in seconds) for when the message file was created.
1920
*/
20-
val createdAt: Int? = null,
21+
@SerialName("created_at") val createdAt: Int? = null,
2122
/**
2223
* The ID of the message that the File is attached to.
2324
*/
24-
val messageId: MessageId? = null,
25+
@SerialName("message_id") val messageId: MessageId? = null,
2526
)

0 commit comments

Comments
 (0)