Skip to content

Commit e7959de

Browse files
committed
Fix Realtime runtime errors
Several problems with the generated code: * Realtime spec (https://platform.openai.com/docs/api-reference/realtime) allows some null fields to have special meaning; Moshi defaults to not sending null fields; add SerializeNull to allow some fields to be sent null. * Session `max_response_output_tokens` (https://platform.openai.com/docs/api-reference/realtime-sessions/create#realtime-sessions-create-max_response_output_tokens) can be either a number (1.4096) or `"inf"`; Make `RealtimeSessionMaxResponseOutputTokens` support both. * Server is sending several undocumented values; add `in_progress`, `audio` * Server is sending null `previous_item_id`; make `previous_item_id` nullable * Removing unnecessary imports instead of renaming any
1 parent fca2c0a commit e7959de

21 files changed

+169
-168
lines changed

lib/src/main/kotlin/com/openai/infrastructure/Serializer.kt

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,72 @@
11
package com.openai.infrastructure
22

3+
import com.openai.models.RealtimeSessionMaxResponseOutputTokensAdapter
4+
import com.squareup.moshi.JsonAdapter
5+
import com.squareup.moshi.JsonQualifier
6+
import com.squareup.moshi.JsonReader
7+
import com.squareup.moshi.JsonWriter
38
import com.squareup.moshi.Moshi
9+
import com.squareup.moshi.Types
10+
import com.squareup.moshi.adapter
411
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
12+
import java.lang.reflect.Type
13+
14+
//
15+
//region SerializeNull https://stackoverflow.com/a/71877976/252308
16+
//
17+
18+
@Retention(AnnotationRetention.RUNTIME)
19+
@JsonQualifier
20+
annotation class SerializeNull {
21+
companion object {
22+
class Factory : JsonAdapter.Factory {
23+
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
24+
val nextAnnotations = Types.nextAnnotations(annotations, SerializeNull::class.java)
25+
return if (nextAnnotations == null) {
26+
null
27+
} else {
28+
NullIfNullJsonAdapter<Any>(moshi.nextAdapter(this, type, nextAnnotations))
29+
}
30+
}
31+
}
32+
33+
class NullIfNullJsonAdapter<T>(private val delegate: JsonAdapter<T>) : JsonAdapter<T>() {
34+
override fun fromJson(reader: JsonReader): T? {
35+
return delegate.fromJson(reader)
36+
}
37+
38+
override fun toJson(writer: JsonWriter, value: T?) {
39+
if (value == null) {
40+
val serializeNulls: Boolean = writer.serializeNulls
41+
writer.serializeNulls = true
42+
try {
43+
delegate.toJson(writer, value)
44+
} finally {
45+
writer.serializeNulls = serializeNulls
46+
}
47+
} else {
48+
delegate.toJson(writer, value)
49+
}
50+
}
51+
}
52+
}
53+
}
54+
55+
//
56+
//endregion
57+
//
558

659
object Serializer {
760
@JvmStatic
861
val moshiBuilder: Moshi.Builder = Moshi.Builder()
62+
.add(SerializeNull.Companion.Factory())
963
.add(OffsetDateTimeAdapter())
1064
.add(LocalDateTimeAdapter())
1165
.add(LocalDateAdapter())
1266
.add(UUIDAdapter())
1367
.add(ByteArrayAdapter())
1468
.add(URIAdapter())
69+
.add(RealtimeSessionMaxResponseOutputTokensAdapter())
1570
.add(KotlinJsonAdapterFactory())
1671
.add(BigDecimalAdapter())
1772
.add(BigIntegerAdapter())

lib/src/main/kotlin/com/openai/models/RealtimeConversationItem.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515

1616
package com.openai.models
1717

18-
import com.openai.models.RealtimeConversationItemContentInner
19-
2018
import com.squareup.moshi.Json
2119
import com.squareup.moshi.JsonClass
2220

@@ -60,7 +58,7 @@ data class RealtimeConversationItem (
6058

6159
/* The content of the message, applicable for `message` items. - Message items of role `system` support only `input_text` content - Message items of role `user` support `input_text` and `input_audio` content - Message items of role `assistant` support `text` content. */
6260
@Json(name = "content")
63-
val content: kotlin.collections.List<RealtimeConversationItemContentInner>? = null,
61+
val content: kotlin.collections.List<RealtimeConversationItemContent>? = null,
6462

6563
/* The ID of the function call (for `function_call` and `function_call_output` items). If passed on a `function_call_output` item, the server will check that a `function_call` item with the same ID exists in the conversation history. */
6664
@Json(name = "call_id")
@@ -108,7 +106,8 @@ data class RealtimeConversationItem (
108106
@JsonClass(generateAdapter = false)
109107
enum class Status(val value: kotlin.String) {
110108
@Json(name = "completed") completed("completed"),
111-
@Json(name = "incomplete") incomplete("incomplete");
109+
@Json(name = "incomplete") incomplete("incomplete"),
110+
@Json(name = "in_progress") inProgress("in_progress");
112111
}
113112
/**
114113
* The role of the message sender (`user`, `assistant`, `system`), only applicable for `message` items.

lib/src/main/kotlin/com/openai/models/RealtimeConversationItemContentInner.kt renamed to lib/src/main/kotlin/com/openai/models/RealtimeConversationItemContent.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ import com.squareup.moshi.JsonClass
3030
*/
3131

3232

33-
data class RealtimeConversationItemContentInner (
33+
data class RealtimeConversationItemContent (
3434

3535
/* The content type (`input_text`, `input_audio`, `item_reference`, `text`). */
3636
@Json(name = "type")
37-
val type: RealtimeConversationItemContentInner.Type? = null,
37+
val type: RealtimeConversationItemContent.Type? = null,
3838

3939
/* The text content, used for `input_text` and `text` content types. */
4040
@Json(name = "text")
@@ -61,6 +61,7 @@ data class RealtimeConversationItemContentInner (
6161
*/
6262
@JsonClass(generateAdapter = false)
6363
enum class Type(val value: kotlin.String) {
64+
@Json(name = "audio") audio("audio"),
6465
@Json(name = "input_audio") input_audio("input_audio"),
6566
@Json(name = "input_text") input_text("input_text"),
6667
@Json(name = "item_reference") item_reference("item_reference"),

lib/src/main/kotlin/com/openai/models/RealtimeResponse.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ data class RealtimeResponse (
8484
@Json(name = "completed") completed("completed"),
8585
@Json(name = "cancelled") cancelled("cancelled"),
8686
@Json(name = "failed") failed("failed"),
87-
@Json(name = "incomplete") incomplete("incomplete");
87+
@Json(name = "incomplete") incomplete("incomplete"),
88+
@Json(name = "in_progress") inProgress("in_progress");
8889
}
8990

9091
}

lib/src/main/kotlin/com/openai/models/RealtimeResponseCreateParams.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515

1616
package com.openai.models
1717

18-
import com.openai.models.RealtimeConversationItem
19-
import com.openai.models.RealtimeResponseCreateParamsConversation
20-
import com.openai.models.RealtimeResponseCreateParamsMaxResponseOutputTokens
21-
import com.openai.models.RealtimeResponseCreateParamsToolsInner
22-
2318
import com.squareup.moshi.Json
2419
import com.squareup.moshi.JsonClass
2520

@@ -71,7 +66,7 @@ data class RealtimeResponseCreateParams (
7166
val temperature: java.math.BigDecimal? = null,
7267

7368
@Json(name = "max_response_output_tokens")
74-
val maxResponseOutputTokens: RealtimeResponseCreateParamsMaxResponseOutputTokens? = null,
69+
val maxResponseOutputTokens: RealtimeSessionMaxResponseOutputTokens? = null,
7570

7671
@Json(name = "conversation")
7772
val conversation: RealtimeResponseCreateParamsConversation? = null,
@@ -123,6 +118,15 @@ data class RealtimeResponseCreateParams (
123118
@Json(name = "g711_ulaw") g711_ulaw("g711_ulaw"),
124119
@Json(name = "g711_alaw") g711_alaw("g711_alaw");
125120
}
121+
/**
122+
* Controls which conversation the response is added to. Currently supports `auto` and `none`, with `auto` as the default value. The `auto` value means that the contents of the response will be added to the default conversation. Set this to `none` to create an out-of-band response which will not add items to default conversation.
123+
*
124+
*/
125+
@JsonClass(generateAdapter = false)
126+
enum class RealtimeResponseCreateParamsConversation {
127+
auto,
128+
none,
129+
}
126130

127131
}
128132

lib/src/main/kotlin/com/openai/models/RealtimeResponseCreateParamsConversation.kt

Lines changed: 0 additions & 34 deletions
This file was deleted.

lib/src/main/kotlin/com/openai/models/RealtimeResponseCreateParamsMaxResponseOutputTokens.kt

Lines changed: 0 additions & 34 deletions
This file was deleted.

lib/src/main/kotlin/com/openai/models/RealtimeServerEventConversationItemCreated.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ data class RealtimeServerEventConversationItemCreated (
4242

4343
/* The ID of the preceding item in the Conversation context, allows the client to understand the order of the conversation. */
4444
@Json(name = "previous_item_id")
45-
val previousItemId: kotlin.String,
45+
val previousItemId: kotlin.String? = null,
4646

4747
@Json(name = "item")
4848
val item: RealtimeConversationItem

lib/src/main/kotlin/com/openai/models/RealtimeServerEventInputAudioBufferCommitted.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ data class RealtimeServerEventInputAudioBufferCommitted (
4141

4242
/* The ID of the preceding item after which the new item will be inserted. */
4343
@Json(name = "previous_item_id")
44-
val previousItemId: kotlin.String,
44+
val previousItemId: kotlin.String? = null,
4545

4646
/* The ID of the user message item that will be created. */
4747
@Json(name = "item_id")

lib/src/main/kotlin/com/openai/models/RealtimeSession.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@
1515

1616
package com.openai.models
1717

18-
import com.openai.models.RealtimeResponseCreateParamsMaxResponseOutputTokens
19-
import com.openai.models.RealtimeResponseCreateParamsToolsInner
20-
import com.openai.models.RealtimeSessionInputAudioTranscription
21-
import com.openai.models.RealtimeSessionModel
22-
import com.openai.models.RealtimeSessionTurnDetection
18+
import com.openai.infrastructure.SerializeNull
2319

2420
import com.squareup.moshi.Json
2521
import com.squareup.moshi.JsonClass
@@ -72,9 +68,11 @@ data class RealtimeSession (
7268
@Json(name = "output_audio_format")
7369
val outputAudioFormat: RealtimeSession.OutputAudioFormat? = null,
7470

71+
@SerializeNull
7572
@Json(name = "input_audio_transcription")
7673
val inputAudioTranscription: RealtimeSessionInputAudioTranscription? = null,
7774

75+
@SerializeNull
7876
@Json(name = "turn_detection")
7977
val turnDetection: RealtimeSessionTurnDetection? = null,
8078

@@ -91,7 +89,7 @@ data class RealtimeSession (
9189
val temperature: java.math.BigDecimal? = null,
9290

9391
@Json(name = "max_response_output_tokens")
94-
val maxResponseOutputTokens: RealtimeResponseCreateParamsMaxResponseOutputTokens? = null
92+
val maxResponseOutputTokens: RealtimeSessionMaxResponseOutputTokens? = null
9593

9694
) {
9795

0 commit comments

Comments
 (0)