Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c41eeeb

Browse files
add moderations endpoint
1 parent fd4b5dd commit c41eeeb

File tree

8 files changed

+164
-21
lines changed

8 files changed

+164
-21
lines changed

‎src/main/kotlin/com/cjcrafter/openai/OpenAI.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.cjcrafter.openai.completions.CompletionResponseChunk
1010
import com.cjcrafter.openai.embeddings.EmbeddingsRequest
1111
import com.cjcrafter.openai.embeddings.EmbeddingsResponse
1212
import com.cjcrafter.openai.files.*
13+
import com.cjcrafter.openai.moderations.ModerationHandler
1314
import com.cjcrafter.openai.threads.ThreadHandler
1415
import com.cjcrafter.openai.threads.message.TextAnnotation
1516
import com.cjcrafter.openai.util.OpenAIDslMarker
@@ -135,6 +136,19 @@ interface OpenAI {
135136
@Contract(pure = true)
136137
fun files(): FileHandler = files
137138

139+
/**
140+
* Returns the handler for the moderations endpoint. This handler can be used
141+
* to create moderations.
142+
*/
143+
val moderations: ModerationHandler
144+
145+
/**
146+
* Returns the handler for the moderations endpoint. This method is purely
147+
* syntactic sugar for Java users.
148+
*/
149+
@Contract(pure = true)
150+
fun moderations(): ModerationHandler = moderations
151+
138152
/**
139153
* Returns the handler for the assistants endpoint. This handler can be used
140154
* to create, retrieve, and delete assistants.

‎src/main/kotlin/com/cjcrafter/openai/OpenAIImpl.kt

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import com.cjcrafter.openai.completions.CompletionResponseChunk
99
import com.cjcrafter.openai.embeddings.EmbeddingsRequest
1010
import com.cjcrafter.openai.embeddings.EmbeddingsResponse
1111
import com.cjcrafter.openai.files.*
12+
import com.cjcrafter.openai.moderations.ModerationHandler
13+
import com.cjcrafter.openai.moderations.ModerationHandlerImpl
1214
import com.cjcrafter.openai.threads.ThreadHandler
1315
import com.cjcrafter.openai.threads.ThreadHandlerImpl
1416
import com.fasterxml.jackson.databind.JavaType
@@ -127,23 +129,28 @@ open class OpenAIImpl @ApiStatus.Internal constructor(
127129
return requestHelper.executeRequest(httpRequest, EmbeddingsResponse::class.java)
128130
}
129131

130-
privatevar files0:FileHandlerImpl?=null
131-
overrideval files:FileHandler
132-
get() = files0 ?:FileHandlerImpl(requestHelper, FILES_ENDPOINT).also { files0 = it }
132+
overrideval files:FileHandler by lazy {
133+
FileHandlerImpl(requestHelper, FILES_ENDPOINT)
134+
}
133135

134-
privatevar assistants0:AssistantHandlerImpl?=null
135-
overrideval assistants:AssistantHandler
136-
get() = assistants0 ?:AssistantHandlerImpl(requestHelper, ASSISTANTS_ENDPOINT).also { assistants0 = it }
136+
overrideval moderations:ModerationHandler by lazy {
137+
ModerationHandlerImpl(requestHelper, MODERATIONS_ENDPOINT)
138+
}
137139

138-
private var threads0: ThreadHandlerImpl? = null
139-
override val threads: ThreadHandler
140-
get() = threads0 ?: ThreadHandlerImpl(requestHelper, THREADS_ENDPOINT).also { threads0 = it }
140+
override val assistants: AssistantHandler by lazy {
141+
AssistantHandlerImpl(requestHelper, ASSISTANTS_ENDPOINT)
142+
}
143+
144+
override val threads: ThreadHandler by lazy {
145+
ThreadHandlerImpl(requestHelper, THREADS_ENDPOINT)
146+
}
141147

142148
companion object {
143149
const val COMPLETIONS_ENDPOINT = "v1/completions"
144150
const val CHAT_ENDPOINT = "v1/chat/completions"
145151
const val EMBEDDINGS_ENDPOINT = "v1/embeddings"
146152
const val FILES_ENDPOINT = "v1/files"
153+
const val MODERATIONS_ENDPOINT = "v1/moderations"
147154
const val ASSISTANTS_ENDPOINT = "v1/assistants"
148155
const val THREADS_ENDPOINT = "v1/threads"
149156
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.cjcrafter.openai.moderations
2+
3+
import com.cjcrafter.openai.util.OpenAIDslMarker
4+
5+
/**
6+
* Represents a request to create a new moderation request.
7+
*
8+
* @property input The input to moderate
9+
* @property model The model to use for moderation
10+
*/
11+
data class CreateModerationRequest internal constructor(
12+
var input: Any,
13+
var model: String? = null
14+
) {
15+
16+
@OpenAIDslMarker
17+
class Builder internal constructor() {
18+
private var input: Any? = null
19+
private var model: String? = null
20+
21+
/**
22+
* Sets the input to moderate.
23+
*
24+
* @param input The input to moderate
25+
*/
26+
fun input(input: String) = apply { this.input = input }
27+
28+
/**
29+
* Sets the input to moderate.
30+
*
31+
* @param input The input to moderate
32+
*/
33+
fun input(input: List<String>) = apply { this.input = input }
34+
35+
/**
36+
* Sets the model to use for moderation.
37+
*
38+
* @param model The model to use for moderation
39+
*/
40+
fun model(model: String) = apply { this.model = model }
41+
42+
/**
43+
* Builds the [CreateModerationRequest] instance.
44+
*/
45+
fun build(): CreateModerationRequest {
46+
return CreateModerationRequest(
47+
input = input ?: throw IllegalStateException("input must be defined to use CreateModerationRequest"),
48+
model = model
49+
)
50+
}
51+
}
52+
53+
companion object {
54+
/**
55+
* Returns a builder to construct a [CreateModerationRequest] instance.
56+
*/
57+
@JvmStatic
58+
fun builder() = Builder()
59+
}
60+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.cjcrafter.openai.moderations
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
5+
/**
6+
* A moderation object returned by the moderations api.
7+
*
8+
* @property id The id of the moderation request. Always starts with "modr-".
9+
* @property model The model which was used to moderate the content.
10+
* @property results The results of the moderation request.
11+
* @constructor Create empty Moderation
12+
*/
13+
data class Moderation(
14+
@JsonProperty(required = true) val id: String,
15+
@JsonProperty(required = true) val model: String,
16+
@JsonProperty(required = true) val results: Results,
17+
) {
18+
/**
19+
* The results of the moderation request.
20+
*
21+
* @property flagged If any categories were flagged.
22+
* @property categories The categories that were flagged.
23+
* @property categoryScores The scores of each category.
24+
* @constructor Create empty Results
25+
*/
26+
data class Results(
27+
@JsonProperty(required = true) val flagged: Boolean,
28+
@JsonProperty(required = true) val categories: Map<String, Boolean>,
29+
@JsonProperty("category_scores", required = true) val categoryScores: Map<String, Double>,
30+
)
31+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.cjcrafter.openai.moderations
2+
3+
/**
4+
* Handler used to interact with [Moderation] objects.
5+
*/
6+
interface ModerationHandler {
7+
8+
/**
9+
* Creates a new moderation request with the given options.
10+
*
11+
* @param request The values of the moderation to create
12+
* @return The created moderation
13+
*/
14+
fun create(request: CreateModerationRequest): Moderation
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.cjcrafter.openai.moderations
2+
3+
import com.cjcrafter.openai.RequestHelper
4+
5+
class ModerationHandlerImpl(
6+
private val requestHelper: RequestHelper,
7+
private val endpoint: String,
8+
): ModerationHandler {
9+
override fun create(request: CreateModerationRequest): Moderation {
10+
val httpRequest = requestHelper.buildRequest(request, endpoint).build()
11+
return requestHelper.executeRequest(httpRequest, Moderation::class.java)
12+
}
13+
}

‎src/test/kotlin/com/cjcrafter/openai/chat/MockedChatStreamTest.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package com.cjcrafter.openai.chat
22

33
import com.cjcrafter.openai.MockedTest
44
import com.cjcrafter.openai.chat.ChatMessage.Companion.toSystemMessage
5+
import com.cjcrafter.openai.chat.tool.FunctionToolCall
6+
import com.cjcrafter.openai.chat.tool.Tool
7+
import com.cjcrafter.openai.chat.tool.ToolCall
58
import okhttp3.mockwebserver.MockResponse
69
import org.junit.jupiter.api.Assertions.assertEquals
710
import org.junit.jupiter.api.Test
@@ -46,9 +49,9 @@ class MockedChatStreamTest : MockedTest() {
4649

4750
// Assertions
4851
assertEquals(ChatUser.ASSISTANT, toolMessage.role, "Tool call should be from the assistant")
49-
assertEquals(ToolType.FUNCTION, toolMessage.toolCalls?.get(0)?.type, "Tool call should be a function")
50-
assertEquals("solve_math_problem", toolMessage.toolCalls?.get(0)?.function?.name)
51-
assertEquals("3/2", toolMessage.toolCalls?.get(0)?.function?.tryParseArguments()?.get("equation")?.asText())
52+
assertEquals(Tool.Type.FUNCTION, toolMessage.toolCalls?.get(0)?.type, "Tool call should be a function")
53+
assertEquals("solve_math_problem", (toolMessage.toolCalls?.get(0) as?FunctionToolCall)?.function?.name)
54+
assertEquals("3/2", (toolMessage.toolCalls?.get(0) as?FunctionToolCall)?.function?.tryParseArguments()?.get("equation")?.asText())
5255

5356
assertEquals(ChatUser.ASSISTANT, message.role, "Message should be from the assistant")
5457
assertEquals("The result of 3 divided by 2 is 1.5.", message.content)

‎src/test/kotlin/com/cjcrafter/openai/chat/tool/FunctionCallTest.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class FunctionCallTest {
1919
name("enum_checker")
2020
description("This function is used to test the enum parameter")
2121
addEnumParameter("enum", mutableListOf("a", "b", "c"))
22-
}.toTool()
22+
}
2323
)
2424
@Language("json")
2525
val json = "{\"name\": \"enum_checker\", \"arguments\": \"{\\\"enum\\\": \\\"d\\\"}\"}" // d is not a valid enum
@@ -37,7 +37,7 @@ class FunctionCallTest {
3737
name("enum_checker")
3838
description("This function is used to test the enum parameter")
3939
addEnumParameter("enum", mutableListOf("a", "b", "c"))
40-
}.toTool()
40+
}
4141
)
4242
@Language("json")
4343
val json = "{\"name\": \"enum_checker\", \"arguments\": \"{\\\"enum\\\": \\\"a\\\"}\"}" // a is a valid enum
@@ -55,7 +55,7 @@ class FunctionCallTest {
5555
name("integer_checker")
5656
description("This function is used to test the integer parameter")
5757
addIntegerParameter("integer", "test parameter")
58-
}.toTool()
58+
}
5959
)
6060
@Language("json")
6161
val json = "{\"name\": \"integer_checker\", \"arguments\": \"{\\\"integer\\\": \\\"not an integer\\\"}\"}" // not an integer
@@ -73,7 +73,7 @@ class FunctionCallTest {
7373
name("integer_checker")
7474
description("This function is used to test the integer parameter")
7575
addIntegerParameter("integer", "test parameter")
76-
}.toTool()
76+
}
7777
)
7878
@Language("json")
7979
val json = "{\"name\": \"integer_checker\", \"arguments\": \"{\\\"integer\\\": 1}\"}" // 1 is an integer
@@ -91,7 +91,7 @@ class FunctionCallTest {
9191
name("boolean_checker")
9292
description("This function is used to test the boolean parameter")
9393
addBooleanParameter("is_true", "test parameter")
94-
}.toTool()
94+
}
9595
)
9696
@Language("json")
9797
val json = "{\"name\": \"boolean_checker\", \"arguments\": \"{\\\"boolean\\\": \\\"not a boolean\\\"}\"}" // not a boolean
@@ -109,7 +109,7 @@ class FunctionCallTest {
109109
name("boolean_checker")
110110
description("This function is used to test the boolean parameter")
111111
addBooleanParameter("is_true", "test parameter")
112-
}.toTool()
112+
}
113113
)
114114
@Language("json")
115115
val json = "{\"name\": \"boolean_checker\", \"arguments\": \"{\\\"is_true\\\": true}\"}" // true is a boolean
@@ -128,7 +128,7 @@ class FunctionCallTest {
128128
description("This function is used to test the required parameter")
129129
addIntegerParameter("required", "test parameter", required = true)
130130
addBooleanParameter("not_required", "test parameter")
131-
}.toTool()
131+
}
132132
)
133133
@Language("json")
134134
val json = "{\"name\": \"required_parameter_function\", \"arguments\": \"{\\\"not_required\\\": true}\"}" // missing required parameter
@@ -147,7 +147,7 @@ class FunctionCallTest {
147147
description("This function is used to test the required parameter")
148148
addIntegerParameter("required", "test parameter", required = true)
149149
addBooleanParameter("not_required", "test parameter")
150-
}.toTool()
150+
}
151151
)
152152
@Language("json")
153153
val json = "{\"name\": \"required_parameter_function\", \"arguments\": \"{\\\"required\\\": 1, \\\"not_required\\\": true}\"}" // has required parameter
@@ -165,7 +165,7 @@ class FunctionCallTest {
165165
name("function_name_checker")
166166
description("This function is used to test the function name")
167167
noParameters()
168-
}.toTool()
168+
}
169169
)
170170
@Language("json")
171171
val json = "{\"name\": \"invalid_function_name\", \"arguments\": \"{}\"}" // invalid function name

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /