Skip to content

Commit dddcdec

Browse files
committed
Fix for #967: Resolve ResponsesModel union to plain model name in URL path
`PrepareRequest.modelNameOrNull()` called `toString()` on whatever `model()` returned, so a `ResponsesModel` leaked its debug form into the Azure deployment path (`ResponsesModel{string=gpt-5.4}` instead of `gpt-5.4`). Resolve the union through its visitor and add tests for the string, chat, and responses-only variants.
1 parent d3191b0 commit dddcdec

2 files changed

Lines changed: 55 additions & 3 deletions

File tree

openai-java-core/src/main/kotlin/com/openai/core/PrepareRequest.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ package com.openai.core
55
import com.openai.azure.addPathSegmentsForAzure
66
import com.openai.azure.replaceBearerTokenForAzure
77
import com.openai.core.http.HttpRequest
8+
import com.openai.models.ChatModel
9+
import com.openai.models.ResponsesModel
810
import java.util.Optional
911
import java.util.concurrent.CompletableFuture
1012
import kotlin.reflect.full.declaredFunctions
@@ -49,8 +51,29 @@ internal fun Params.modelNameOrNull(): String? {
4951
null
5052
}
5153

52-
return when (modelName) {
53-
is Optional<*> -> modelName.orElse(null)?.toString()
54-
else -> modelName?.toString()
54+
val unwrappedModelName =
55+
when (modelName) {
56+
is Optional<*> -> modelName.orElse(null)
57+
else -> modelName
58+
}
59+
60+
return when (unwrappedModelName) {
61+
// The `toString()` of a union type is a debug representation, so resolve its variant.
62+
is ResponsesModel -> unwrappedModelName.modelNameOrNull()
63+
else -> unwrappedModelName?.toString()
5564
}
5665
}
66+
67+
private fun ResponsesModel.modelNameOrNull(): String? =
68+
accept(
69+
object : ResponsesModel.Visitor<String?> {
70+
override fun visitString(string: String): String = string
71+
72+
override fun visitChat(chat: ChatModel): String = chat.toString()
73+
74+
override fun visitOnly(only: ResponsesModel.ResponsesOnlyModel): String =
75+
only.toString()
76+
77+
override fun unknown(json: JsonValue?): String? = null
78+
}
79+
)

openai-java-core/src/test/kotlin/com/openai/core/PrepareRequestTest.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package com.openai.core
22

33
import com.openai.core.http.Headers
44
import com.openai.core.http.QueryParams
5+
import com.openai.models.ChatModel
6+
import com.openai.models.ResponsesModel
57
import com.openai.models.chat.completions.ChatCompletionListParams
68
import com.openai.models.embeddings.EmbeddingCreateParams
79
import com.openai.models.embeddings.EmbeddingModel
10+
import com.openai.models.responses.ResponseCreateParams
811
import com.openai.models.uploads.UploadCancelParams
912
import org.assertj.core.api.Assertions.assertThat
1013
import org.assertj.core.api.Assertions.assertThatNoException
@@ -106,4 +109,30 @@ internal class PrepareRequestTest {
106109
assertThatNoException().isThrownBy { params.modelNameOrNull() }
107110
assertThat(params.modelNameOrNull()).isNull()
108111
}
112+
113+
@Test
114+
fun modelUnionStringNotNull() {
115+
val params = ResponseCreateParams.builder().model("my-model").input("Hello, world!").build()
116+
117+
assertThat(params.modelNameOrNull()).isEqualTo("my-model")
118+
}
119+
120+
@Test
121+
fun modelUnionChatNotNull() {
122+
val params =
123+
ResponseCreateParams.builder().model(ChatModel.GPT_4O).input("Hello, world!").build()
124+
125+
assertThat(params.modelNameOrNull()).isEqualTo("gpt-4o")
126+
}
127+
128+
@Test
129+
fun modelUnionOnlyNotNull() {
130+
val params =
131+
ResponseCreateParams.builder()
132+
.model(ResponsesModel.ResponsesOnlyModel.O1_PRO)
133+
.input("Hello, world!")
134+
.build()
135+
136+
assertThat(params.modelNameOrNull()).isEqualTo("o1-pro")
137+
}
109138
}

0 commit comments

Comments
 (0)