Skip to content

Commit 541a330

Browse files
committed
feat: response api entity
1 parent 222781e commit 541a330

File tree

64 files changed

+433
-187
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+433
-187
lines changed

api/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>top.bella</groupId>
88
<artifactId>openai-java</artifactId>
9-
<version>0.23.50</version>
9+
<version>0.23.51</version>
1010
</parent>
1111
<packaging>jar</packaging>
1212
<artifactId>openai-api</artifactId>

api/src/main/java/com/theokanning/openai/assistants/message/MessageContent.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import com.fasterxml.jackson.annotation.JsonProperty;
55
import com.theokanning.openai.assistants.message.content.ImageFile;
66
import com.theokanning.openai.assistants.message.content.Text;
7+
import com.theokanning.openai.completion.chat.ChatToolCall;
78
import com.theokanning.openai.completion.chat.ImageUrl;
9+
import com.theokanning.openai.completion.chat.ToolMessage;
10+
import com.theokanning.openai.response.tool.ToolCall;
811
import lombok.Data;
912

1013

@@ -17,7 +20,7 @@
1720
public class MessageContent {
1821

1922
/**
20-
* image_file/text
23+
* image_url/image_file/text/tool_call/tool_result
2124
*/
2225
String type;
2326

@@ -42,14 +45,30 @@ public class MessageContent {
4245
@JsonInclude(JsonInclude.Include.NON_NULL)
4346
ImageUrl imageUrl;
4447

48+
@JsonProperty("tool_call")
49+
@JsonInclude(JsonInclude.Include.NON_NULL)
50+
ChatToolCall toolCall;
51+
52+
@JsonProperty("tool_result")
53+
@JsonInclude(JsonInclude.Include.NON_NULL)
54+
ToolMessage toolResult;
55+
4556
public boolean empty() {
4657
switch (type) {
4758
case "image_file":
4859
return imageFile == null;
4960
case "image_url":
5061
return imageUrl == null;
62+
case "tool_call":
63+
return toolCall == null;
64+
case "toolResult":
65+
return toolResult == null;
5166
default:
5267
return text == null || text.getValue() == null || text.getValue().trim().isEmpty();
5368
}
5469
}
70+
71+
public boolean isVision() {
72+
return type.equals("image_url") || type.equals("image_file");
73+
}
5574
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.theokanning.openai.assistants.run;
2+
3+
import lombok.Data;
4+
5+
import java.util.List;
6+
7+
@Data
8+
public class AllowedTools {
9+
String mode;
10+
List<Tool> tools;
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.theokanning.openai.assistants.run;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class Tool {
7+
private String type = "function";
8+
private Function function;
9+
}

api/src/main/java/com/theokanning/openai/assistants/run/ToolChoice.java

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22

33
import com.fasterxml.jackson.core.JsonGenerator;
44
import com.fasterxml.jackson.core.JsonParser;
5+
import com.fasterxml.jackson.core.JsonProcessingException;
56
import com.fasterxml.jackson.core.JsonToken;
67
import com.fasterxml.jackson.databind.DeserializationContext;
78
import com.fasterxml.jackson.databind.JsonDeserializer;
89
import com.fasterxml.jackson.databind.JsonSerializer;
10+
import com.fasterxml.jackson.databind.ObjectMapper;
911
import com.fasterxml.jackson.databind.SerializerProvider;
1012
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
1113
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
14+
import com.theokanning.openai.completion.CompletionRequest;
15+
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
1216
import lombok.Data;
1317

1418
import java.io.IOException;
19+
import java.util.List;
1520

1621
/**
1722
* https://platform.openai.com/docs/guides/function-calling/function-calling-behavior
@@ -33,12 +38,18 @@ public class ToolChoice {
3338
*/
3439
Function function;
3540

41+
/**
42+
* which tolls is allowed
43+
*/
44+
AllowedTools allowedTools;
45+
3646
/**
3747
* The type of the tool. If type is function, the function name must be set
3848
* enum: none/auto/function/required
3949
*/
4050
String type;
4151

52+
4253
private ToolChoice(String type) {
4354
this.type = type;
4455
}
@@ -51,6 +62,14 @@ public ToolChoice(Function function) {
5162
this.function = function;
5263
}
5364

65+
public ToolChoice(AllowedTools allowedTools) {
66+
this.type = "allowed_tools";
67+
if (allowedTools == null || allowedTools.getTools().isEmpty()) {
68+
throw new IllegalArgumentException("allowedTools must not be empty");
69+
}
70+
this.allowedTools = allowedTools;
71+
}
72+
5473
public static class Deserializer extends JsonDeserializer<ToolChoice> {
5574

5675
@Override
@@ -71,9 +90,17 @@ public ToolChoice deserialize(JsonParser jsonParser, DeserializationContext dese
7190
if (jsonParser.getCurrentToken() == JsonToken.START_OBJECT) {
7291
// 处理对象的情况
7392
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
74-
if (jsonParser.getCurrentName().equals("function")) {
93+
String fieldName = jsonParser.getCurrentName();
94+
if ("function".equals(fieldName)) {
95+
jsonParser.nextToken();
96+
ToolChoice toolChoice = new ToolChoice("function");
97+
toolChoice.function = parseFunction(jsonParser);
98+
return toolChoice;
99+
} else if ("allowed_tools".equals(fieldName)) {
75100
jsonParser.nextToken();
76-
return new ToolChoice(parseFunction(jsonParser));
101+
ToolChoice toolChoice = new ToolChoice("allowed_tools");
102+
toolChoice.allowedTools = parseAllowedTools(jsonParser);
103+
return toolChoice;
77104
}
78105
}
79106
}
@@ -91,12 +118,56 @@ private Function parseFunction(JsonParser jsonParser) throws IOException {
91118
function.setName(jsonParser.nextTextValue());
92119
}
93120
}
94-
jsonParser.nextToken();
95121
return function;
96122
}
97123
//抛出异常
98124
throw new IllegalArgumentException("Invalid Function");
99125
}
126+
127+
private AllowedTools parseAllowedTools(JsonParser jsonParser) throws IOException {
128+
if (jsonParser.getCurrentToken() == JsonToken.START_OBJECT) {
129+
AllowedTools allowedTools = new AllowedTools();
130+
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
131+
String fieldName = jsonParser.getCurrentName();
132+
if ("mode".equals(fieldName)) {
133+
allowedTools.setMode(jsonParser.nextTextValue());
134+
} else if ("tools".equals(fieldName)) {
135+
jsonParser.nextToken();
136+
allowedTools.setTools(parseToolsList(jsonParser));
137+
}
138+
}
139+
return allowedTools;
140+
}
141+
throw new IllegalArgumentException("Invalid AllowedTools");
142+
}
143+
144+
private List<Tool> parseToolsList(JsonParser jsonParser) throws IOException {
145+
if (jsonParser.getCurrentToken() == JsonToken.START_ARRAY) {
146+
List<Tool> tools = new java.util.ArrayList<>();
147+
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
148+
tools.add(parseTool(jsonParser));
149+
}
150+
return tools;
151+
}
152+
throw new IllegalArgumentException("Invalid Tools Array");
153+
}
154+
155+
private Tool parseTool(JsonParser jsonParser) throws IOException {
156+
if (jsonParser.getCurrentToken() == JsonToken.START_OBJECT) {
157+
Tool tool = new Tool();
158+
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
159+
String fieldName = jsonParser.getCurrentName();
160+
if ("type".equals(fieldName)) {
161+
tool.setType(jsonParser.nextTextValue());
162+
} else if ("function".equals(fieldName)) {
163+
jsonParser.nextToken();
164+
tool.setFunction(parseFunction(jsonParser));
165+
}
166+
}
167+
return tool;
168+
}
169+
throw new IllegalArgumentException("Invalid Tool");
170+
}
100171
}
101172

102173
public static class Serializer extends JsonSerializer<ToolChoice> {
@@ -109,14 +180,61 @@ public void serialize(ToolChoice toolChoice, JsonGenerator jsonGenerator, Serial
109180
case "required":
110181
jsonGenerator.writeString(type);
111182
break;
183+
case "function":
184+
jsonGenerator.writeStartObject();
185+
jsonGenerator.writeStringField("type", type);
186+
jsonGenerator.writeObjectField("function", toolChoice.getFunction());
187+
jsonGenerator.writeEndObject();
188+
break;
189+
case "allowed_tools":
190+
jsonGenerator.writeStartObject();
191+
jsonGenerator.writeStringField("type", type);
192+
jsonGenerator.writeObjectField("allowed_tools", toolChoice.getAllowedTools());
193+
jsonGenerator.writeEndObject();
194+
break;
112195
default:
113196
jsonGenerator.writeStartObject();
114197
jsonGenerator.writeStringField("type", type);
115-
if (toolChoice.getType().equals("function")) {
116-
jsonGenerator.writeObjectField("function", toolChoice.getFunction());
117-
}
118198
jsonGenerator.writeEndObject();
119199
}
120200
}
121201
}
202+
203+
public static void main(String[] args) throws JsonProcessingException {
204+
ObjectMapper mapper = new ObjectMapper();
205+
String str = "{\n"
206+
+ "\t\"type\": \"allowed_tools\",\n"
207+
+ "\t\"allowed_tools\": {\n"
208+
+ "\t\t\"mode\": \"auto\",\n"
209+
+ "\t\t\"tools\": [{\n"
210+
+ "\t\t\t\"type\": \"function\",\n"
211+
+ "\t\t\t\"function\": {\n"
212+
+ "\t\t\t\t\"name\": \"get_weather\"\n"
213+
+ "\t\t\t}\n"
214+
+ "\t\t}, {\n"
215+
+ "\t\t\t\"type\": \"function\",\n"
216+
+ "\t\t\t\"function\": {\n"
217+
+ "\t\t\t\t\"name\": \"get_time\"\n"
218+
+ "\t\t\t}\n"
219+
+ "\t\t}]\n"
220+
+ "\t}\n"
221+
+ "}";
222+
ToolChoice toolChoice = mapper.readValue(str, ToolChoice.class);
223+
System.out.println(mapper.writeValueAsString(toolChoice));
224+
String str2 = "{\n"
225+
+ "\t\"type\": \"function\",\n"
226+
+ "\t\"function\": {\n"
227+
+ "\t\t\"name\": \"get_weather\"\n"
228+
+ "\t}\n"
229+
+ "}";
230+
ToolChoice toolChoice2 = mapper.readValue(str2, ToolChoice.class);
231+
System.out.println(mapper.writeValueAsString(toolChoice2));
232+
233+
String str3 = "{\n"
234+
+ "\t\"tool_choice\": \"auto\"\n"
235+
+ "}";
236+
ChatCompletionRequest request = mapper.readValue(str3, ChatCompletionRequest.class);
237+
System.out.println(mapper.writeValueAsString(request));
238+
}
239+
122240
}

api/src/main/java/com/theokanning/openai/response/CreateResponseRequest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.fasterxml.jackson.databind.ObjectMapper;
77
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
88
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
9+
import com.theokanning.openai.assistants.run.TruncationStrategy;
910
import com.theokanning.openai.completion.chat.ChatResponseFormat;
1011
import com.theokanning.openai.response.content.InputImage;
1112
import com.theokanning.openai.response.content.InputMessage;
@@ -148,7 +149,8 @@ public class CreateResponseRequest {
148149
/**
149150
* Context truncation strategy.
150151
*/
151-
private String truncation = "disabled";
152+
@JsonProperty("truncation_strategy")
153+
private TruncationStrategy truncationStrategy;
152154

153155
/**
154156
* Additional data to include in response.
@@ -160,6 +162,8 @@ public class CreateResponseRequest {
160162
*/
161163
private Map<String, String> metadata = new HashMap<>();
162164

165+
private String user;
166+
163167
public static void main(String[] args) throws JsonProcessingException {
164168
String e = "invalid type";
165169
String str = "{\"model\":\"o3\",\"input\":[{\"role\":\"system\",\"content\":\"你是一个智能体\"},{\"type\":\"message\",\"role\":\"user\",\"content\":\"北京现在的天气怎么样?\"},{\"type\":\"function_call\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\": \\\"北京\\\", \\\"unit\\\": \\\"celsius\\\"}\",\"status\":\"completed\",\"call_id\":\"call_weather456\"},{\"type\":\"function_call_output\",\"output\":\"{\\\"temperature\\\": 15, \\\"condition\\\": \\\"晴朗\\\"}\",\"status\":\"completed\",\"call_id\":\"call_weather456\"},{\"type\":\"message\",\"role\":\"user\",\"content\":\"比较一下上海的天气\"},{\"type\":\"message\",\"id\":\"msg_def456\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Machine learning is a subset of artificial intelligence that enables computers to learn and improve from experience.\",\"annotations\":[{\"type\":\"url_citation\",\"url\":\"https://example.com/ml-guide\",\"title\":\"Introduction to Machine Learning\",\"start_index\":0,\"end_index\":50}]}],\"status\":\"completed\"},{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_image\",\"image_url\":\"https://example.com/ahdhadh.jpg\"},{\"type\":\"input_text\",\"text\":\"识别图片并且生成一张相同的图片。\"}]},{\"type\":\"image_generation_call\",\"id\":\"call_cacda\",\"result\":\"https://example.com/ahdhadhadadacf.jpg\",\"status\":\"completed\"},{\"id\":\"msg_def456121313\",\"role\":\"assistant\",\"content\":[{\"type\":\"refusal\",\"refusal\":\"test\"}],\"status\":\"incomplete\"},{\"type\":\"message\",\"role\":\"user\",\"content\":\"再次生成\"}],\"store\":false,\"stream\":true,\"temperature\":1.0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Retrieves current weather for the given location.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"object\",\"properties\":{\"x\":{\"type\":\"string\"},\"y\":{\"type\":\"string\"}},\"description\":\"City and country e.g. Bogotá, Colombia\",\"required\":[\"x\",\"y\"],\"additionalProperties\":false},\"units\":{\"type\":\"string\",\"enum\":[\"celsius\",\"fahrenheit\"],\"description\":\"Units the temperature will be returned in.\"}},\"required\":[\"location\",\"units\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"file_search\"},{\"type\":\"custom\",\"name\":\"test\",\"description\":\"test\",\"format\":{\"type\":\"text\"}}],\"reasoning\":{\"effort\":\"medium\"},\"background\":false,\"truncation\":\"disabled\",\"metadata\":{},\"top_p\":1.0,\"parallel_tool_calls\":true,\"text\":{\"format\":{\"type\":\"json_schema\",\"json_schema\":{\"name\":\"analysis_result\",\"schema\":{\"type\":\"object\",\"properties\":{\"image_analysis\":{\"type\":\"string\"},\"weather_info\":{\"type\":\"object\"},\"summary\":{\"type\":\"string\"}}}}}},\"tool_choice\":{\"type\":\"allowed_tools\",\"mode\":\"auto\",\"tools\":[{\"type\":\"file_search\"},{\"type\":\"function\",\"name\":\"get_weather\"}]}}";

api/src/main/java/com/theokanning/openai/response/Response.java

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fasterxml.jackson.annotation.JsonProperty;
55
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
66
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
7+
import com.theokanning.openai.Usage;
78
import com.theokanning.openai.completion.chat.ChatResponseFormat;
89
import com.theokanning.openai.response.tool.definition.ToolDefinition;
910
import lombok.AllArgsConstructor;
@@ -199,45 +200,6 @@ public static class ReasoningFormat {
199200
private String summary;
200201
}
201202

202-
@Data
203-
@Builder
204-
@NoArgsConstructor
205-
@AllArgsConstructor
206-
public static class Usage {
207-
@JsonProperty("input_tokens")
208-
private Integer inputTokens;
209-
210-
@JsonProperty("output_tokens")
211-
private Integer outputTokens;
212-
213-
@JsonProperty("total_tokens")
214-
private Integer totalTokens;
215-
216-
@JsonProperty("input_tokens_details")
217-
private InputTokensDetails inputTokensDetails;
218-
219-
@JsonProperty("output_tokens_details")
220-
private OutputTokensDetails outputTokensDetails;
221-
}
222-
223-
@Data
224-
@Builder
225-
@NoArgsConstructor
226-
@AllArgsConstructor
227-
public static class InputTokensDetails {
228-
@JsonProperty("cached_tokens")
229-
private Integer cachedTokens;
230-
}
231-
232-
@Data
233-
@Builder
234-
@NoArgsConstructor
235-
@AllArgsConstructor
236-
public static class OutputTokensDetails {
237-
@JsonProperty("reasoning_tokens")
238-
private Integer reasoningTokens;
239-
}
240-
241203
@Data
242204
@Builder
243205
@NoArgsConstructor

api/src/main/java/com/theokanning/openai/response/ResponseItem.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,5 @@
4646
@JsonSubTypes.Type(value = ItemReference.class, name = "item_reference")
4747
})
4848
public interface ResponseItem extends ConversationItem {
49+
String getId();
4950
}

api/src/main/java/com/theokanning/openai/response/content/Reasoning.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.theokanning.openai.response.content;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.fasterxml.jackson.annotation.JsonInclude;
45
import com.fasterxml.jackson.annotation.JsonProperty;
56
import com.theokanning.openai.response.ItemStatus;
@@ -66,6 +67,10 @@ public String getType() {
6667
public static class SummaryText {
6768
private String type = "summary_text";
6869
private String text;
70+
@JsonIgnore
71+
private String reasoningSignature;
72+
@JsonIgnore
73+
private String redactedReasoningContent;
6974
}
7075

7176
@Data
@@ -75,5 +80,9 @@ public static class SummaryText {
7580
public static class ReasoningText {
7681
private String type = "reasoning_text";
7782
private String text;
83+
@JsonIgnore
84+
private String reasoningSignature;
85+
@JsonIgnore
86+
private String redactedReasoningContent;
7887
}
7988
}

0 commit comments

Comments
 (0)