The following OpenAI-API Input Request works as expected when invoked from curl
but gives error 400
when invoked from a Java program. The Java source code files follow.
Kindly advise.
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-4-vision-preview",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What’s in this image?"
},
{
"type": "image_url",
"image_url": {
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
}
}
]
}
],
"max_tokens": 300
}'
Here are my 3 Java files.
/////////////////////////////////////////////////////////////////////
// First File: Prompt.java
/////////////////////////////////////////////////////////////////////
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*
* Class that represents the Prompt.
* Inner classes: FileUrl, Content, Message
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Prompt {
/**
* Inner Class FileUrl
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public static class FileUrl {
@JsonProperty("url")
public String url;
public FileUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return JsonConverter.toJson(this);
}
}
/**
* Inner Class Content
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Content {
@JsonProperty("type")
public String type;
@JsonProperty("text")
public String text;
@JsonProperty("image_url")
public FileUrl image_url;
/**
*
*/
public Content(String type, String text) {
this.type = type;
this.text = text;
this.image_url = null;
}
/**
*
*/
public Content(String type, FileUrl image_url) {
this.type = type;
this.image_url = image_url;
this.text = null;
}
/**
*
*/
@Override
public String toString() {
return JsonConverter.toJson(this);
}
}
/**
* Inner Class Message
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Message {
@JsonProperty("role")
public String role;
@JsonProperty("content")
public List<Content> content;
/**
*
*/
public Message(String role, List<Content> content) {
this.role = role;
this.content = content;
}
/**
*
*/
@Override
public String toString() {
return JsonConverter.toJson(this);
}
}
/**
* Outer Class
*
*/
@JsonProperty("model")
public String model;
@JsonProperty("messages")
public List<Message> messages;
@JsonProperty("max_tokens")
public int max_tokens;
/**
*
*/
public Prompt(String model, List<Message> messages, int max_tokens) {
this.model = model;
this.messages = messages;
this.max_tokens = max_tokens;
}
/**
*
*/
@Override
public String toString() {
return JsonConverter.toJson(this);
}
}
/////////////////////////////////////////////////////////////////////
// Second File: JsonConverter.java
/////////////////////////////////////////////////////////////////////
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Converts a Java object to its JSON string representation,
* which is neatly formatted. Performs some additional functions like
* - removing null fields
* - neatly formatting the JSON String representation
*/
public class JsonConverter {
public static String toJson(Object object) {
String jsonStr = null;
try {
ObjectMapper objectMapper = new ObjectMapper();
if (object instanceof String) {
jsonStr = (String) object;
}
else {
jsonStr = objectMapper.writeValueAsString(object);
}
if(jsonStr != null) {
jsonStr = removeNullFields(jsonStr, objectMapper);
}
JsonNode jsonNode = objectMapper.readTree(jsonStr);
objectMapper.enable(com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT); // Enable pretty printing
jsonStr = objectMapper.writeValueAsString(jsonNode);
}
catch (Exception e) {
e.printStackTrace();
}
return jsonStr;
}
protected static String removeNullFields(String jsonString, ObjectMapper mapper) throws IOException {
// Parse the JSON string into a JsonNode
JsonNode rootNode = mapper.readTree(jsonString);
// Remove null fields from the JsonNode
JsonNode cleanedNode = removeNullFields(rootNode);
// Convert the updated JsonNode back to a JSON string
return mapper.writeValueAsString(cleanedNode);
}
/**
*
*/
private static JsonNode removeNullFields(JsonNode node) {
if (node.isObject()) {
ObjectNode objectNode = (ObjectNode) node;
List<String> keysToRemove = new ArrayList<>();
Iterator<String> fieldNames = objectNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode fieldValue = objectNode.get(fieldName);
if (fieldValue.isNull()) {
keysToRemove.add(fieldName);
}
else {
removeNullFields(fieldValue);
}
}
keysToRemove.forEach(objectNode::remove);
}
else if (node.isArray()) {
ArrayNode arrayNode = (ArrayNode) node;
List<Integer> indicesToRemove = new ArrayList<>();
for (int i = 0; i < arrayNode.size(); i++) {
JsonNode element = arrayNode.get(i);
if (element.isNull()) {
indicesToRemove.add(i);
}
else {
removeNullFields(element);
}
}
indicesToRemove.forEach(arrayNode::remove);
}
return node;
}
}
/////////////////////////////////////////////////////////////////////
// Third File: ImageInputClient.java
/////////////////////////////////////////////////////////////////////
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Client Application
*/
public class ImageInputClient {
public static void main(String[] args) {
boolean debug = true;
String openAIKey = "my-openai-key-blah-blah-blah";
String endpoint = "https://api.openai.com/v1/chat/completions";
String model = "gpt-4-vision-preview";
int maxTokens = 300;
// Start building the request
Prompt.Content contentObj1 = new Prompt.Content("text", "What’s in this image?");
Prompt.Content contentObj2 = new Prompt.Content("image_url", new Prompt.FileUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"));
List<Prompt.Content> contentList = new ArrayList<>();
contentList.add(contentObj1);
contentList.add(contentObj2);
Prompt.Message messageObj = new Prompt.Message("user", contentList);
List<Prompt.Message> messages = new ArrayList<>();
messages.add(messageObj);
// Create Input JSON string
String jsonStr = null;
try {
Prompt prompt = new Prompt(model, messages, maxTokens);
if(debug) {
System.out.println(prompt);
System.out.println("\n\n");
}
jsonStr = JsonConverter.toJson(prompt);
}
catch (Exception e) {
e.printStackTrace();
}
try {
URL url = new URL(endpoint);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Authorization", "Bearer " + openAIKey);
connection.setDoOutput(true);
if(debug) {
System.out.println("Request Headers:");
connection.getRequestProperties().forEach((key, value) -> System.out.println(key + ": " + value));
}
OutputStream outputStream = connection.getOutputStream();
outputStream.write(jsonStr.getBytes());
outputStream.flush();
outputStream.close();
if(debug) {
System.out.println("Request Body:\n");
System.out.println(jsonStr);
}
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
System.out.println(JsonConverter.toJson(response.toString()));
}
else {
System.out.println("Error: " + responseCode);
}
connection.disconnect();
}
catch (Exception e) {
e.printStackTrace();
}
}
}