From f3dfaf786097fd94d7787f93b40f06389593a954 Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Tue, 18 Jun 2024 14:35:30 -0700 Subject: [PATCH 01/11] draft --- .../ai/openai/models/ChatRequestUserMessage.java | 6 +++--- .../models/ChatRequestMessageUnitTests.java | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index 42a2fbb43508..c0fd3259c7e4 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -65,7 +65,7 @@ public ChatRequestUserMessage(ChatMessageContentItem[] content) { * @param content The collection of structured content associated with the message. */ public ChatRequestUserMessage(List content) { - this(BinaryData.fromObject(content.stream().map(BinaryData::fromObject).collect(Collectors.toList()))); + this(BinaryData.fromObject(content)); } /** @@ -123,7 +123,7 @@ public ChatRole getRole() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeBinaryField("content", this.content.toBytes()); + jsonWriter.writeRawField("content", this.content.toString()); jsonWriter.writeStringField("role", this.role == null ? null : this.role.toString()); jsonWriter.writeStringField("name", this.name); return jsonWriter.writeEndObject(); @@ -148,7 +148,7 @@ public static ChatRequestUserMessage fromJson(JsonReader jsonReader) throws IOEx String fieldName = reader.getFieldName(); reader.nextToken(); if ("content".equals(fieldName)) { - content = reader.getNullable(nonNullReader -> BinaryData.fromObject(nonNullReader.readUntyped())); + content = BinaryData.fromObject(reader.readUntyped()); } else if ("role".equals(fieldName)) { role = ChatRole.fromString(reader.getString()); } else if ("name".equals(fieldName)) { diff --git a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java index 68155c778121..c1334f52a7b0 100644 --- a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java +++ b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java @@ -3,8 +3,11 @@ package com.azure.ai.openai.models; +import com.azure.core.util.BinaryData; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -15,9 +18,20 @@ public class ChatRequestMessageUnitTests { String name = "Alice"; @Test public void testChatRequestUserMessage() { + // String type content ChatRequestUserMessage chatRequestUserMessage = new ChatRequestUserMessage(content).setName(name); - assertEquals(content, chatRequestUserMessage.getContent().toString()); + + + // List type content + final ChatRequestUserMessage user = new ChatRequestUserMessage(List.of( + new ChatMessageTextContentItem("text"), + new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) + )); + final String content = BinaryData.fromObject(user).toString(); + System.out.println(content); + final ChatRequestUserMessage converted = BinaryData.fromString(content).toObject(ChatRequestUserMessage.class); + System.out.println(converted); } @Test From 585084fa09ca36eb5ed7329e2007461befa28bfe Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Wed, 19 Jun 2024 12:23:55 -0700 Subject: [PATCH 02/11] use common test class --- .../openai/ChatCompletionsFunctionCall.java | 49 +++++++++++++--- .../azure/ai/openai/StreamingToolCall.java | 45 ++------------- .../ai/openai/models/FunctionArguments.java | 56 +++++++++++++++++++ .../ai/openai/models/FunctionParameters.java | 3 - .../ai/openai/models/FunctionProperties.java | 2 - .../models/FutureTemperatureParameters.java | 48 ++++++++++++++++ .../models/FutureTemperatureProperties.java | 51 +++++++++++++++++ .../azure/ai/openai/models/StringField.java | 54 ++++++++++++++++++ ...GetChatCompletionsToolCallAsyncSample.java | 48 ++-------------- .../GetChatCompletionsToolCallSample.java | 48 ++-------------- .../implementation/EmbeddingUtilsTests.java | 5 +- .../models/ChatRequestMessageUnitTests.java | 4 +- 12 files changed, 269 insertions(+), 144 deletions(-) create mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java create mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java create mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java create mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java index 478622256c37..9aedf1cc4635 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java @@ -16,12 +16,16 @@ import com.azure.ai.openai.models.FunctionDefinition; import com.azure.ai.openai.models.FunctionParameters; import com.azure.ai.openai.models.FunctionProperties; +import com.azure.core.annotation.Generated; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -124,12 +128,10 @@ private static int getCurrentWeather(WeatherLocation weatherLocation) { } // WeatherLocation is used for this sample. This describes the parameter of the function you want to use. - private static class WeatherLocation { - @JsonProperty(value = "unit") String unit; - @JsonProperty(value = "location") String location; - @JsonCreator - WeatherLocation(@JsonProperty(value = "unit") String unit, - @JsonProperty(value = "location") String location) { + private static class WeatherLocation implements JsonSerializable { + String unit; + String location; + WeatherLocation(String unit, String location) { this.unit = unit; this.location = location; } @@ -141,5 +143,36 @@ public String getUnit() { public String getLocation() { return location; } + + /** + * {@inheritDoc} + */ + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + jsonWriter.writeStringField("unit", this.unit); + jsonWriter.writeStringField("location", this.location); + return jsonWriter.writeEndObject(); + } + + @Generated + public static WeatherLocation fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + String unit = null; + String location = null; + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + if ("unit".equals(fieldName)) { + unit = reader.getString(); + } else if ("location".equals(fieldName)) { + location = reader.getString(); + } else { + reader.skipChildren(); + } + } + return new WeatherLocation(unit, location); + }); + } } } diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java index 9281ceb6895b..2d425ac6b12c 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java @@ -16,14 +16,14 @@ import com.azure.ai.openai.models.ChatRequestToolMessage; import com.azure.ai.openai.models.ChatRequestUserMessage; import com.azure.ai.openai.models.CompletionsFinishReason; +import com.azure.ai.openai.models.FunctionArguments; import com.azure.ai.openai.models.FunctionCall; import com.azure.ai.openai.models.FunctionDefinition; +import com.azure.ai.openai.models.FutureTemperatureParameters; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; import com.azure.core.util.IterableStream; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; @@ -117,9 +117,9 @@ public static void main(String[] args) { // As an additional step, you may want to deserialize the parameters, so you can call your function FunctionArguments parameters = BinaryData.fromString(functionArguments.toString()).toObject(FunctionArguments.class); - System.out.println("Location Name: " + parameters.locationName); - System.out.println("Date: " + parameters.date); - String functionCallResult = futureTemperature(parameters.locationName, parameters.date); + System.out.println("Location Name: " + parameters.getLocationName()); + System.out.println("Date: " + parameters.getDate()); + String functionCallResult = futureTemperature(parameters.getLocationName(), parameters.getDate()); // This message contains the information that will allow the LLM to resume the text generation ChatRequestToolMessage toolRequestMessage = new ChatRequestToolMessage(functionCallResult, toolCallId); @@ -168,39 +168,4 @@ private static FunctionDefinition getFutureTemperatureFunctionDefinition() { functionDefinition.setParameters(BinaryData.fromObject(parameters)); return functionDefinition; } - - private static class FunctionArguments { - @JsonProperty(value = "location_name") - private String locationName; - - @JsonProperty(value = "date") - private String date; - } - - private static class FutureTemperatureParameters { - @JsonProperty(value = "type") - private String type = "object"; - - @JsonProperty(value = "properties") - private FutureTemperatureProperties properties = new FutureTemperatureProperties(); - } - - private static class FutureTemperatureProperties { - @JsonProperty(value = "unit") StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); - @JsonProperty(value = "location_name") StringField locationName = new StringField("The name of the location to get the future temperature for."); - @JsonProperty(value = "date") StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); - } - - private static class StringField { - @JsonProperty(value = "type") - private final String type = "string"; - - @JsonProperty(value = "description") - private String description; - - @JsonCreator - StringField(@JsonProperty(value = "description") String description) { - this.description = description; - } - } } diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java new file mode 100644 index 000000000000..cce2ca423cec --- /dev/null +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.openai.models; + +import com.azure.core.annotation.Generated; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +public class FunctionArguments implements JsonSerializable { + private String locationName; + + private String date; + + public String getLocationName() { + return locationName; + } + + public String getDate() { + return date; + } + + /** + * {@inheritDoc} + */ + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + jsonWriter.writeStringField("location_name", this.locationName); + jsonWriter.writeStringField("date", this.date); + return jsonWriter.writeEndObject(); + } + + @Generated + public static FunctionArguments fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + FunctionArguments deserializedFunctionArguments = new FunctionArguments(); + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + if ("location_name".equals(fieldName)) { + deserializedFunctionArguments.locationName = reader.getString(); + } else if ("date".equals(fieldName)) { + deserializedFunctionArguments.date = reader.getString(); + } else { + reader.skipChildren(); + } + } + return deserializedFunctionArguments; + }); + } +} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java index dd51a52de848..eabbf0519c53 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java @@ -3,8 +3,6 @@ package com.azure.ai.openai.models; -import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; import java.util.Map; @@ -22,7 +20,6 @@ public class FunctionParameters { private Map properties; /* Required properties */ - @JsonProperty(value = "required") private List requiredPropertyNames; /** diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java index cd68afd2d379..c84b4f0bed45 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java @@ -4,7 +4,6 @@ package com.azure.ai.openai.models; import java.util.List; -import com.fasterxml.jackson.annotation.JsonProperty; /** * This is the property for creating the 'FunctionDefinition'. @@ -18,7 +17,6 @@ public class FunctionProperties { // Description of the Property private String description; // Enum values for the Property - @JsonProperty("enum") private List enumString; /** diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java new file mode 100644 index 000000000000..0e46f79917c9 --- /dev/null +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.openai.models; + +import com.azure.core.annotation.Generated; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +public class FutureTemperatureParameters implements JsonSerializable { + private String type = "object"; + + private FutureTemperatureProperties properties = new FutureTemperatureProperties(); + + /** + * {@inheritDoc} + */ + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + jsonWriter.writeStringField("type", this.type); + jsonWriter.writeJsonField("properties", this.properties); + return jsonWriter.writeEndObject(); + } + + @Generated + public static FutureTemperatureParameters fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + FutureTemperatureParameters deserializedFutureTemperatureParameters = new FutureTemperatureParameters(); + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + if ("type".equals(fieldName)) { + deserializedFutureTemperatureParameters.type = reader.getString(); + } else if ("properties".equals(fieldName)) { + deserializedFutureTemperatureParameters.properties = FutureTemperatureProperties.fromJson(reader); + } else { + reader.skipChildren(); + } + } + return deserializedFutureTemperatureParameters; + }); + } +} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java new file mode 100644 index 000000000000..d6870fa3242f --- /dev/null +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.openai.models; + +import com.azure.core.annotation.Generated; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +public class FutureTemperatureProperties implements JsonSerializable { + StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); + StringField locationName = new StringField("The name of the location to get the future temperature for."); + StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); + + /** + * {@inheritDoc} + */ + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + jsonWriter.writeJsonField("unit", this.unit); + jsonWriter.writeJsonField("location_name", this.locationName); + jsonWriter.writeJsonField("date", this.date); + return jsonWriter.writeEndObject(); + } + + @Generated + public static FutureTemperatureProperties fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + FutureTemperatureProperties deserializedFutureTemperatureProperties = new FutureTemperatureProperties(); + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + if ("unit".equals(fieldName)) { + deserializedFutureTemperatureProperties.unit = StringField.fromJson(reader); + } else if ("location_name".equals(fieldName)) { + deserializedFutureTemperatureProperties.locationName = StringField.fromJson(reader); + } else if ("date".equals(fieldName)) { + deserializedFutureTemperatureProperties.date = StringField.fromJson(reader); + } else { + reader.skipChildren(); + } + } + return deserializedFutureTemperatureProperties; + }); + } +} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java new file mode 100644 index 000000000000..cad83b42ba61 --- /dev/null +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.openai.models; + +import com.azure.core.annotation.Generated; +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +public class StringField implements JsonSerializable { + private final String type = "string"; + + private String description; + + StringField(String description) { + this.description = description; + } + + /** + * {@inheritDoc} + */ + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + jsonWriter.writeStringField("type", this.type); + jsonWriter.writeStringField("description", this.description); + return jsonWriter.writeEndObject(); + } + + @Generated + public static StringField fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + String type = null; + String description = null; + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + if ("type".equals(fieldName)) { + type = reader.getString(); + } else if ("properties".equals(fieldName)) { + description = reader.getString(); + } else { + reader.skipChildren(); + } + } + + return new StringField(description); + }); + } +} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java index 9079af476a79..2cc60282c3b4 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java @@ -16,12 +16,12 @@ import com.azure.ai.openai.models.ChatRequestToolMessage; import com.azure.ai.openai.models.ChatRequestUserMessage; import com.azure.ai.openai.models.CompletionsFinishReason; +import com.azure.ai.openai.models.FunctionArguments; import com.azure.ai.openai.models.FunctionDefinition; +import com.azure.ai.openai.models.FutureTemperatureParameters; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; @@ -75,10 +75,10 @@ public static void main(String[] args) throws InterruptedException { // As an additional step, you may want to deserialize the parameters, so you can call your function FunctionArguments parameters = BinaryData.fromString(functionArguments).toObject(FunctionArguments.class); - System.out.println("Location Name: " + parameters.locationName); - System.out.println("Date: " + parameters.date); + System.out.println("Location Name: " + parameters.getLocationName()); + System.out.println("Date: " + parameters.getDate()); - String functionCallResult = futureTemperature(parameters.locationName, parameters.date); + String functionCallResult = futureTemperature(parameters.getLocationName(), parameters.getDate()); ChatRequestAssistantMessage assistantMessage = new ChatRequestAssistantMessage(""); assistantMessage.setToolCalls(choice.getMessage().getToolCalls()); @@ -126,42 +126,4 @@ private static FunctionDefinition getFutureTemperatureFunctionDefinition() { functionDefinition.setParameters(BinaryData.fromObject(parameters)); return functionDefinition; } - - private static class FunctionArguments { - @JsonProperty(value = "location_name") - private String locationName; - - @JsonProperty(value = "date") - private String date; - } - - private static class FutureTemperatureParameters { - @JsonProperty(value = "type") - private String type = "object"; - - @JsonProperty(value = "properties") - private FutureTemperatureProperties properties = new FutureTemperatureProperties(); - } - - private static class FutureTemperatureProperties { - @JsonProperty(value = "unit") - StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); - @JsonProperty(value = "location_name") - StringField locationName = new StringField("The name of the location to get the future temperature for."); - @JsonProperty(value = "date") - StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); - } - - private static class StringField { - @JsonProperty(value = "type") - private final String type = "string"; - - @JsonProperty(value = "description") - private String description; - - @JsonCreator - StringField(@JsonProperty(value = "description") String description) { - this.description = description; - } - } } diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java index 74f7fb02d37b..653230c49316 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java @@ -17,12 +17,12 @@ import com.azure.ai.openai.models.ChatRequestToolMessage; import com.azure.ai.openai.models.ChatRequestUserMessage; import com.azure.ai.openai.models.CompletionsFinishReason; +import com.azure.ai.openai.models.FunctionArguments; import com.azure.ai.openai.models.FunctionDefinition; +import com.azure.ai.openai.models.FutureTemperatureParameters; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; @@ -74,10 +74,10 @@ public static void main(String[] args) { // As an additional step, you may want to deserialize the parameters, so you can call your function FunctionArguments parameters = BinaryData.fromString(functionArguments).toObject(FunctionArguments.class); - System.out.println("Location Name: " + parameters.locationName); - System.out.println("Date: " + parameters.date); + System.out.println("Location Name: " + parameters.getLocationName()); + System.out.println("Date: " + parameters.getDate()); - String functionCallResult = futureTemperature(parameters.locationName, parameters.date); + String functionCallResult = futureTemperature(parameters.getLocationName(), parameters.getDate()); ChatRequestAssistantMessage assistantMessage = new ChatRequestAssistantMessage(""); assistantMessage.setToolCalls(choice.getMessage().getToolCalls()); @@ -117,42 +117,4 @@ private static FunctionDefinition getFutureTemperatureFunctionDefinition() { functionDefinition.setParameters(BinaryData.fromObject(parameters)); return functionDefinition; } - - private static class FunctionArguments { - @JsonProperty(value = "location_name") - private String locationName; - - @JsonProperty(value = "date") - private String date; - } - - private static class FutureTemperatureParameters { - @JsonProperty(value = "type") - private String type = "object"; - - @JsonProperty(value = "properties") - private FutureTemperatureProperties properties = new FutureTemperatureProperties(); - } - - private static class FutureTemperatureProperties { - @JsonProperty(value = "unit") - StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); - @JsonProperty(value = "location_name") - StringField locationName = new StringField("The name of the location to get the future temperature for."); - @JsonProperty(value = "date") - StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); - } - - private static class StringField { - @JsonProperty(value = "type") - private final String type = "string"; - - @JsonProperty(value = "description") - private String description; - - @JsonCreator - StringField(@JsonProperty(value = "description") String description) { - this.description = description; - } - } } diff --git a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java index 56404b53c098..4ea15df4d6e3 100644 --- a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java +++ b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java @@ -4,7 +4,6 @@ package com.azure.ai.openai.implementation; import com.azure.core.util.BinaryData; -import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -39,7 +38,7 @@ public void testEmbeddingItemConvertBase64ToFloatList() { } @Test - public void testBinaryDataHasNoEncodingFormat() throws JsonProcessingException { + public void testBinaryDataHasNoEncodingFormat() { // Arrange String jsonWithoutEncodingFormat = "{\"input\":[\"Your text string goes here\"]}"; String expectedJson = "{\"input\":[\"Your text string goes here\"],\"encoding_format\":\"base64\"}"; @@ -50,7 +49,7 @@ public void testBinaryDataHasNoEncodingFormat() throws JsonProcessingException { } @Test - public void testBinaryDataHasEncodingFormat() throws JsonProcessingException { + public void testBinaryDataHasEncodingFormat() { // Arrange String jsonWithEncodingFormat = "{\"input\":[\"Your text string goes here\"],\"encoding_format\":\"float\"}"; // Act diff --git a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java index c1334f52a7b0..9b54cfff512b 100644 --- a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java +++ b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java @@ -6,7 +6,7 @@ import com.azure.core.util.BinaryData; import org.junit.jupiter.api.Test; -import java.util.List; +import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -24,7 +24,7 @@ public void testChatRequestUserMessage() { // List type content - final ChatRequestUserMessage user = new ChatRequestUserMessage(List.of( + final ChatRequestUserMessage user = new ChatRequestUserMessage(Arrays.asList( new ChatMessageTextContentItem("text"), new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) )); From b926a11519ee2ff5b7f7f07b12486d0bad1c0a2b Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Wed, 19 Jun 2024 17:41:57 -0700 Subject: [PATCH 03/11] Fixed a bug during the serialization and deserialization of the content property in the ChatRequestUserMessage. --- sdk/openai/azure-ai-openai/CHANGELOG.md | 2 + .../openai/models/ChatRequestUserMessage.java | 5 +-- .../models/ChatRequestMessageUnitTests.java | 39 +++++++++++++++---- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/sdk/openai/azure-ai-openai/CHANGELOG.md b/sdk/openai/azure-ai-openai/CHANGELOG.md index 01ce84a563bc..524147a24c91 100644 --- a/sdk/openai/azure-ai-openai/CHANGELOG.md +++ b/sdk/openai/azure-ai-openai/CHANGELOG.md @@ -8,6 +8,8 @@ ### Bugs Fixed +- Fixed a bug during the serialization and deserialization of the `content` property in the `ChatRequestUserMessage`. ([#39204](https://github.com/Azure/azure-sdk-for-java/pull/39204)) + ### Other Changes ## 1.0.0-beta.9 (2024-06-06) diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index c0fd3259c7e4..e913da5701b1 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -9,10 +9,9 @@ import com.azure.json.JsonReader; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; + import java.io.IOException; import java.util.List; -import java.util.stream.Collectors; -import java.util.Arrays; /** * A request chat message representing user input to the assistant. @@ -56,7 +55,7 @@ public ChatRequestUserMessage(String content) { * @param content The collection of structured content associated with the message. */ public ChatRequestUserMessage(ChatMessageContentItem[] content) { - this(BinaryData.fromObject(Arrays.stream(content).map(BinaryData::fromObject).collect(Collectors.toList()))); + this(BinaryData.fromObject(content)); } /** diff --git a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java index 9b54cfff512b..25cab68935f8 100644 --- a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java +++ b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java @@ -8,6 +8,7 @@ import java.util.Arrays; +import static com.azure.ai.openai.models.ChatRole.USER; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -17,21 +18,31 @@ public class ChatRequestMessageUnitTests { String content = "You are a helpful assistant."; String name = "Alice"; @Test - public void testChatRequestUserMessage() { + public void testChatRequestUserMessageContentInString() { // String type content ChatRequestUserMessage chatRequestUserMessage = new ChatRequestUserMessage(content).setName(name); assertEquals(content, chatRequestUserMessage.getContent().toString()); + assertEquals(name, chatRequestUserMessage.getName()); + assertEquals(USER, chatRequestUserMessage.getRole()); + } + @Test + public void testChatRequestUserMessageContentInChatMessageContentItemList() { + ChatMessageContentItem[] content = new ChatMessageContentItem[] { + new ChatMessageTextContentItem("textContent"), + new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) + }; + ChatRequestUserMessage userMessage = new ChatRequestUserMessage(content); + assertChatRequestUserMessage(userMessage); + } - // List type content - final ChatRequestUserMessage user = new ChatRequestUserMessage(Arrays.asList( - new ChatMessageTextContentItem("text"), + @Test + public void testChatRequestUserMessageContentInChatMessageContentItemArray() { + ChatRequestUserMessage userMessage = new ChatRequestUserMessage(Arrays.asList( + new ChatMessageTextContentItem("textContent"), new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) )); - final String content = BinaryData.fromObject(user).toString(); - System.out.println(content); - final ChatRequestUserMessage converted = BinaryData.fromString(content).toObject(ChatRequestUserMessage.class); - System.out.println(converted); + assertChatRequestUserMessage(userMessage); } @Test @@ -57,4 +68,16 @@ public void testChatRequestFunctionMessage() { ChatRequestFunctionMessage chatRequestFunctionMessage = new ChatRequestFunctionMessage(name, content); assertEquals(content, chatRequestFunctionMessage.getContent()); } + + private void assertChatRequestUserMessage(ChatRequestUserMessage userMessage) { + String userMessageInString = BinaryData.fromObject(userMessage).toString(); + assertEquals( + "{\"content\":[{\"text\":\"textContent\",\"type\":\"text\"},{\"image_url\":{\"url\":\"testImage\"},\"type\":\"image_url\"}],\"role\":\"user\"}", + userMessageInString); + ChatRequestUserMessage converted = BinaryData.fromString(userMessageInString) + .toObject(ChatRequestUserMessage.class); + assertEquals(userMessage.getContent().toString(), converted.getContent().toString()); + assertEquals(userMessage.getName(), converted.getName()); + assertEquals(USER, converted.getRole()); + } } From c29ff39d7fa38c17891d15f20d7bf2552288e5b7 Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Wed, 19 Jun 2024 17:52:13 -0700 Subject: [PATCH 04/11] Pass TypeSpec validation --- .../java/com/azure/ai/openai/models/ChatRequestUserMessage.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index e913da5701b1..4410000d1ca7 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -9,7 +9,6 @@ import com.azure.json.JsonReader; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; - import java.io.IOException; import java.util.List; @@ -137,7 +136,6 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { * @throws IllegalStateException If the deserialized JSON object was missing any required properties. * @throws IOException If an error occurs while reading the ChatRequestUserMessage. */ - @Generated public static ChatRequestUserMessage fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { BinaryData content = null; From aee7e8950eec957933bfa9f17fd5093cfe459f61 Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Wed, 19 Jun 2024 18:00:43 -0700 Subject: [PATCH 05/11] revert test source code azure-json migration changes --- .../openai/ChatCompletionsFunctionCall.java | 49 +++------------- .../azure/ai/openai/StreamingToolCall.java | 45 +++++++++++++-- .../ai/openai/models/FunctionArguments.java | 56 ------------------- .../ai/openai/models/FunctionParameters.java | 3 + .../ai/openai/models/FunctionProperties.java | 2 + .../models/FutureTemperatureParameters.java | 48 ---------------- .../models/FutureTemperatureProperties.java | 51 ----------------- .../azure/ai/openai/models/StringField.java | 54 ------------------ ...GetChatCompletionsToolCallAsyncSample.java | 48 ++++++++++++++-- .../GetChatCompletionsToolCallSample.java | 48 ++++++++++++++-- .../implementation/EmbeddingUtilsTests.java | 5 +- 11 files changed, 142 insertions(+), 267 deletions(-) delete mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java delete mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java delete mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java delete mode 100644 sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java index 9aedf1cc4635..478622256c37 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/ChatCompletionsFunctionCall.java @@ -16,16 +16,12 @@ import com.azure.ai.openai.models.FunctionDefinition; import com.azure.ai.openai.models.FunctionParameters; import com.azure.ai.openai.models.FunctionProperties; -import com.azure.core.annotation.Generated; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -128,10 +124,12 @@ private static int getCurrentWeather(WeatherLocation weatherLocation) { } // WeatherLocation is used for this sample. This describes the parameter of the function you want to use. - private static class WeatherLocation implements JsonSerializable { - String unit; - String location; - WeatherLocation(String unit, String location) { + private static class WeatherLocation { + @JsonProperty(value = "unit") String unit; + @JsonProperty(value = "location") String location; + @JsonCreator + WeatherLocation(@JsonProperty(value = "unit") String unit, + @JsonProperty(value = "location") String location) { this.unit = unit; this.location = location; } @@ -143,36 +141,5 @@ public String getUnit() { public String getLocation() { return location; } - - /** - * {@inheritDoc} - */ - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - jsonWriter.writeStringField("unit", this.unit); - jsonWriter.writeStringField("location", this.location); - return jsonWriter.writeEndObject(); - } - - @Generated - public static WeatherLocation fromJson(JsonReader jsonReader) throws IOException { - return jsonReader.readObject(reader -> { - String unit = null; - String location = null; - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - if ("unit".equals(fieldName)) { - unit = reader.getString(); - } else if ("location".equals(fieldName)) { - location = reader.getString(); - } else { - reader.skipChildren(); - } - } - return new WeatherLocation(unit, location); - }); - } } } diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java index 2d425ac6b12c..9281ceb6895b 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/StreamingToolCall.java @@ -16,14 +16,14 @@ import com.azure.ai.openai.models.ChatRequestToolMessage; import com.azure.ai.openai.models.ChatRequestUserMessage; import com.azure.ai.openai.models.CompletionsFinishReason; -import com.azure.ai.openai.models.FunctionArguments; import com.azure.ai.openai.models.FunctionCall; import com.azure.ai.openai.models.FunctionDefinition; -import com.azure.ai.openai.models.FutureTemperatureParameters; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; import com.azure.core.util.IterableStream; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; @@ -117,9 +117,9 @@ public static void main(String[] args) { // As an additional step, you may want to deserialize the parameters, so you can call your function FunctionArguments parameters = BinaryData.fromString(functionArguments.toString()).toObject(FunctionArguments.class); - System.out.println("Location Name: " + parameters.getLocationName()); - System.out.println("Date: " + parameters.getDate()); - String functionCallResult = futureTemperature(parameters.getLocationName(), parameters.getDate()); + System.out.println("Location Name: " + parameters.locationName); + System.out.println("Date: " + parameters.date); + String functionCallResult = futureTemperature(parameters.locationName, parameters.date); // This message contains the information that will allow the LLM to resume the text generation ChatRequestToolMessage toolRequestMessage = new ChatRequestToolMessage(functionCallResult, toolCallId); @@ -168,4 +168,39 @@ private static FunctionDefinition getFutureTemperatureFunctionDefinition() { functionDefinition.setParameters(BinaryData.fromObject(parameters)); return functionDefinition; } + + private static class FunctionArguments { + @JsonProperty(value = "location_name") + private String locationName; + + @JsonProperty(value = "date") + private String date; + } + + private static class FutureTemperatureParameters { + @JsonProperty(value = "type") + private String type = "object"; + + @JsonProperty(value = "properties") + private FutureTemperatureProperties properties = new FutureTemperatureProperties(); + } + + private static class FutureTemperatureProperties { + @JsonProperty(value = "unit") StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); + @JsonProperty(value = "location_name") StringField locationName = new StringField("The name of the location to get the future temperature for."); + @JsonProperty(value = "date") StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); + } + + private static class StringField { + @JsonProperty(value = "type") + private final String type = "string"; + + @JsonProperty(value = "description") + private String description; + + @JsonCreator + StringField(@JsonProperty(value = "description") String description) { + this.description = description; + } + } } diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java deleted file mode 100644 index cce2ca423cec..000000000000 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionArguments.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.ai.openai.models; - -import com.azure.core.annotation.Generated; -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class FunctionArguments implements JsonSerializable { - private String locationName; - - private String date; - - public String getLocationName() { - return locationName; - } - - public String getDate() { - return date; - } - - /** - * {@inheritDoc} - */ - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - jsonWriter.writeStringField("location_name", this.locationName); - jsonWriter.writeStringField("date", this.date); - return jsonWriter.writeEndObject(); - } - - @Generated - public static FunctionArguments fromJson(JsonReader jsonReader) throws IOException { - return jsonReader.readObject(reader -> { - FunctionArguments deserializedFunctionArguments = new FunctionArguments(); - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - if ("location_name".equals(fieldName)) { - deserializedFunctionArguments.locationName = reader.getString(); - } else if ("date".equals(fieldName)) { - deserializedFunctionArguments.date = reader.getString(); - } else { - reader.skipChildren(); - } - } - return deserializedFunctionArguments; - }); - } -} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java index eabbf0519c53..dd51a52de848 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionParameters.java @@ -3,6 +3,8 @@ package com.azure.ai.openai.models; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; import java.util.Map; @@ -20,6 +22,7 @@ public class FunctionParameters { private Map properties; /* Required properties */ + @JsonProperty(value = "required") private List requiredPropertyNames; /** diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java index c84b4f0bed45..cd68afd2d379 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FunctionProperties.java @@ -4,6 +4,7 @@ package com.azure.ai.openai.models; import java.util.List; +import com.fasterxml.jackson.annotation.JsonProperty; /** * This is the property for creating the 'FunctionDefinition'. @@ -17,6 +18,7 @@ public class FunctionProperties { // Description of the Property private String description; // Enum values for the Property + @JsonProperty("enum") private List enumString; /** diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java deleted file mode 100644 index 0e46f79917c9..000000000000 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureParameters.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.ai.openai.models; - -import com.azure.core.annotation.Generated; -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class FutureTemperatureParameters implements JsonSerializable { - private String type = "object"; - - private FutureTemperatureProperties properties = new FutureTemperatureProperties(); - - /** - * {@inheritDoc} - */ - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - jsonWriter.writeStringField("type", this.type); - jsonWriter.writeJsonField("properties", this.properties); - return jsonWriter.writeEndObject(); - } - - @Generated - public static FutureTemperatureParameters fromJson(JsonReader jsonReader) throws IOException { - return jsonReader.readObject(reader -> { - FutureTemperatureParameters deserializedFutureTemperatureParameters = new FutureTemperatureParameters(); - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - if ("type".equals(fieldName)) { - deserializedFutureTemperatureParameters.type = reader.getString(); - } else if ("properties".equals(fieldName)) { - deserializedFutureTemperatureParameters.properties = FutureTemperatureProperties.fromJson(reader); - } else { - reader.skipChildren(); - } - } - return deserializedFutureTemperatureParameters; - }); - } -} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java deleted file mode 100644 index d6870fa3242f..000000000000 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/FutureTemperatureProperties.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.ai.openai.models; - -import com.azure.core.annotation.Generated; -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class FutureTemperatureProperties implements JsonSerializable { - StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); - StringField locationName = new StringField("The name of the location to get the future temperature for."); - StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); - - /** - * {@inheritDoc} - */ - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - jsonWriter.writeJsonField("unit", this.unit); - jsonWriter.writeJsonField("location_name", this.locationName); - jsonWriter.writeJsonField("date", this.date); - return jsonWriter.writeEndObject(); - } - - @Generated - public static FutureTemperatureProperties fromJson(JsonReader jsonReader) throws IOException { - return jsonReader.readObject(reader -> { - FutureTemperatureProperties deserializedFutureTemperatureProperties = new FutureTemperatureProperties(); - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - if ("unit".equals(fieldName)) { - deserializedFutureTemperatureProperties.unit = StringField.fromJson(reader); - } else if ("location_name".equals(fieldName)) { - deserializedFutureTemperatureProperties.locationName = StringField.fromJson(reader); - } else if ("date".equals(fieldName)) { - deserializedFutureTemperatureProperties.date = StringField.fromJson(reader); - } else { - reader.skipChildren(); - } - } - return deserializedFutureTemperatureProperties; - }); - } -} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java deleted file mode 100644 index cad83b42ba61..000000000000 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/models/StringField.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.ai.openai.models; - -import com.azure.core.annotation.Generated; -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class StringField implements JsonSerializable { - private final String type = "string"; - - private String description; - - StringField(String description) { - this.description = description; - } - - /** - * {@inheritDoc} - */ - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - jsonWriter.writeStringField("type", this.type); - jsonWriter.writeStringField("description", this.description); - return jsonWriter.writeEndObject(); - } - - @Generated - public static StringField fromJson(JsonReader jsonReader) throws IOException { - return jsonReader.readObject(reader -> { - String type = null; - String description = null; - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - if ("type".equals(fieldName)) { - type = reader.getString(); - } else if ("properties".equals(fieldName)) { - description = reader.getString(); - } else { - reader.skipChildren(); - } - } - - return new StringField(description); - }); - } -} diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java index 2cc60282c3b4..9079af476a79 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallAsyncSample.java @@ -16,12 +16,12 @@ import com.azure.ai.openai.models.ChatRequestToolMessage; import com.azure.ai.openai.models.ChatRequestUserMessage; import com.azure.ai.openai.models.CompletionsFinishReason; -import com.azure.ai.openai.models.FunctionArguments; import com.azure.ai.openai.models.FunctionDefinition; -import com.azure.ai.openai.models.FutureTemperatureParameters; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; @@ -75,10 +75,10 @@ public static void main(String[] args) throws InterruptedException { // As an additional step, you may want to deserialize the parameters, so you can call your function FunctionArguments parameters = BinaryData.fromString(functionArguments).toObject(FunctionArguments.class); - System.out.println("Location Name: " + parameters.getLocationName()); - System.out.println("Date: " + parameters.getDate()); + System.out.println("Location Name: " + parameters.locationName); + System.out.println("Date: " + parameters.date); - String functionCallResult = futureTemperature(parameters.getLocationName(), parameters.getDate()); + String functionCallResult = futureTemperature(parameters.locationName, parameters.date); ChatRequestAssistantMessage assistantMessage = new ChatRequestAssistantMessage(""); assistantMessage.setToolCalls(choice.getMessage().getToolCalls()); @@ -126,4 +126,42 @@ private static FunctionDefinition getFutureTemperatureFunctionDefinition() { functionDefinition.setParameters(BinaryData.fromObject(parameters)); return functionDefinition; } + + private static class FunctionArguments { + @JsonProperty(value = "location_name") + private String locationName; + + @JsonProperty(value = "date") + private String date; + } + + private static class FutureTemperatureParameters { + @JsonProperty(value = "type") + private String type = "object"; + + @JsonProperty(value = "properties") + private FutureTemperatureProperties properties = new FutureTemperatureProperties(); + } + + private static class FutureTemperatureProperties { + @JsonProperty(value = "unit") + StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); + @JsonProperty(value = "location_name") + StringField locationName = new StringField("The name of the location to get the future temperature for."); + @JsonProperty(value = "date") + StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); + } + + private static class StringField { + @JsonProperty(value = "type") + private final String type = "string"; + + @JsonProperty(value = "description") + private String description; + + @JsonCreator + StringField(@JsonProperty(value = "description") String description) { + this.description = description; + } + } } diff --git a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java index 653230c49316..74f7fb02d37b 100644 --- a/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java +++ b/sdk/openai/azure-ai-openai/src/samples/java/com/azure/ai/openai/usage/GetChatCompletionsToolCallSample.java @@ -17,12 +17,12 @@ import com.azure.ai.openai.models.ChatRequestToolMessage; import com.azure.ai.openai.models.ChatRequestUserMessage; import com.azure.ai.openai.models.CompletionsFinishReason; -import com.azure.ai.openai.models.FunctionArguments; import com.azure.ai.openai.models.FunctionDefinition; -import com.azure.ai.openai.models.FutureTemperatureParameters; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.BinaryData; import com.azure.core.util.Configuration; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; @@ -74,10 +74,10 @@ public static void main(String[] args) { // As an additional step, you may want to deserialize the parameters, so you can call your function FunctionArguments parameters = BinaryData.fromString(functionArguments).toObject(FunctionArguments.class); - System.out.println("Location Name: " + parameters.getLocationName()); - System.out.println("Date: " + parameters.getDate()); + System.out.println("Location Name: " + parameters.locationName); + System.out.println("Date: " + parameters.date); - String functionCallResult = futureTemperature(parameters.getLocationName(), parameters.getDate()); + String functionCallResult = futureTemperature(parameters.locationName, parameters.date); ChatRequestAssistantMessage assistantMessage = new ChatRequestAssistantMessage(""); assistantMessage.setToolCalls(choice.getMessage().getToolCalls()); @@ -117,4 +117,42 @@ private static FunctionDefinition getFutureTemperatureFunctionDefinition() { functionDefinition.setParameters(BinaryData.fromObject(parameters)); return functionDefinition; } + + private static class FunctionArguments { + @JsonProperty(value = "location_name") + private String locationName; + + @JsonProperty(value = "date") + private String date; + } + + private static class FutureTemperatureParameters { + @JsonProperty(value = "type") + private String type = "object"; + + @JsonProperty(value = "properties") + private FutureTemperatureProperties properties = new FutureTemperatureProperties(); + } + + private static class FutureTemperatureProperties { + @JsonProperty(value = "unit") + StringField unit = new StringField("Temperature unit. Can be either Celsius or Fahrenheit. Defaults to Celsius."); + @JsonProperty(value = "location_name") + StringField locationName = new StringField("The name of the location to get the future temperature for."); + @JsonProperty(value = "date") + StringField date = new StringField("The date to get the future temperature for. The format is YYYY-MM-DD."); + } + + private static class StringField { + @JsonProperty(value = "type") + private final String type = "string"; + + @JsonProperty(value = "description") + private String description; + + @JsonCreator + StringField(@JsonProperty(value = "description") String description) { + this.description = description; + } + } } diff --git a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java index 4ea15df4d6e3..56404b53c098 100644 --- a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java +++ b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/implementation/EmbeddingUtilsTests.java @@ -4,6 +4,7 @@ package com.azure.ai.openai.implementation; import com.azure.core.util.BinaryData; +import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -38,7 +39,7 @@ public void testEmbeddingItemConvertBase64ToFloatList() { } @Test - public void testBinaryDataHasNoEncodingFormat() { + public void testBinaryDataHasNoEncodingFormat() throws JsonProcessingException { // Arrange String jsonWithoutEncodingFormat = "{\"input\":[\"Your text string goes here\"]}"; String expectedJson = "{\"input\":[\"Your text string goes here\"],\"encoding_format\":\"base64\"}"; @@ -49,7 +50,7 @@ public void testBinaryDataHasNoEncodingFormat() { } @Test - public void testBinaryDataHasEncodingFormat() { + public void testBinaryDataHasEncodingFormat() throws JsonProcessingException { // Arrange String jsonWithEncodingFormat = "{\"input\":[\"Your text string goes here\"],\"encoding_format\":\"float\"}"; // Act From df43e29c527b6d80a2634cf9dbba879d9b767d8f Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Tue, 25 Jun 2024 13:23:11 -0700 Subject: [PATCH 06/11] pass all tests now --- sdk/openai/azure-ai-openai/README.md | 2 +- sdk/openai/azure-ai-openai/assets.json | 2 +- .../openai/models/ChatRequestUserMessage.java | 5 +- .../models/ChatRequestMessageUnitTests.java | 76 +++++++++++++++++-- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/sdk/openai/azure-ai-openai/README.md b/sdk/openai/azure-ai-openai/README.md index 8a902a237bb8..818edacf0270 100644 --- a/sdk/openai/azure-ai-openai/README.md +++ b/sdk/openai/azure-ai-openai/README.md @@ -56,7 +56,7 @@ In order to interact with the Azure OpenAI Service you'll need to create an inst Azure OpenAI, provide a valid endpoint URI to an Azure OpenAI resource along with a corresponding key credential, token credential, or [Azure Identity][azure_identity] credential that's authorized to use the Azure OpenAI resource. -#### Create a Azure OpenAI client with key credential +#### Create an Azure OpenAI client with key credential Get Azure OpenAI `key` credential from the Azure Portal. ```java readme-sample-createSyncClientKeyCredential diff --git a/sdk/openai/azure-ai-openai/assets.json b/sdk/openai/azure-ai-openai/assets.json index 2f9d3c51964f..f15e2d3e79a8 100644 --- a/sdk/openai/azure-ai-openai/assets.json +++ b/sdk/openai/azure-ai-openai/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/openai/azure-ai-openai", - "Tag": "java/openai/azure-ai-openai_959f9440df" + "Tag": "java/openai/azure-ai-openai_0e2b3dee1d" } diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index 4410000d1ca7..13f98ebb41e5 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -9,6 +9,7 @@ import com.azure.json.JsonReader; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; + import java.io.IOException; import java.util.List; @@ -121,7 +122,7 @@ public ChatRole getRole() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeRawField("content", this.content.toString()); + jsonWriter.writeBinaryField("content", this.content.toBytes()); jsonWriter.writeStringField("role", this.role == null ? null : this.role.toString()); jsonWriter.writeStringField("name", this.name); return jsonWriter.writeEndObject(); @@ -145,7 +146,7 @@ public static ChatRequestUserMessage fromJson(JsonReader jsonReader) throws IOEx String fieldName = reader.getFieldName(); reader.nextToken(); if ("content".equals(fieldName)) { - content = BinaryData.fromObject(reader.readUntyped()); + content = BinaryData.fromBytes(reader.getBinary()); } else if ("role".equals(fieldName)) { role = ChatRole.fromString(reader.getString()); } else if ("name".equals(fieldName)) { diff --git a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java index 25cab68935f8..0c0bc2c6cc5a 100644 --- a/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java +++ b/sdk/openai/azure-ai-openai/src/test/java/com/azure/ai/openai/models/ChatRequestMessageUnitTests.java @@ -10,6 +10,7 @@ import static com.azure.ai.openai.models.ChatRole.USER; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * Unit tests for ChatRequestMessage. @@ -24,23 +25,37 @@ public void testChatRequestUserMessageContentInString() { assertEquals(content, chatRequestUserMessage.getContent().toString()); assertEquals(name, chatRequestUserMessage.getName()); assertEquals(USER, chatRequestUserMessage.getRole()); + + // Test it in ChatCompletionsOptions + ChatCompletionsOptions chatCompletionsOptions = new ChatCompletionsOptions(Arrays.asList(chatRequestUserMessage)); + assertChatCompletionsOptions(chatCompletionsOptions); + } + + @Test + public void testUserMessageContentSameStructureAfterConverted() { + final ChatRequestUserMessage user = new ChatRequestUserMessage(Arrays.asList( + new ChatMessageTextContentItem("textContent"), + new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) + )); + final String userMessageInString = BinaryData.fromObject(user).toString(); + final ChatRequestUserMessage convertedUserMessageFromString = BinaryData.fromString(userMessageInString).toObject(ChatRequestUserMessage.class); + assertEquals(user.getContent().toString(), convertedUserMessageFromString.getContent().toString()); } @Test public void testChatRequestUserMessageContentInChatMessageContentItemList() { - ChatMessageContentItem[] content = new ChatMessageContentItem[] { + ChatRequestUserMessage userMessage = new ChatRequestUserMessage(new ChatMessageContentItem[] { new ChatMessageTextContentItem("textContent"), new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) - }; - ChatRequestUserMessage userMessage = new ChatRequestUserMessage(content); + }); assertChatRequestUserMessage(userMessage); } @Test public void testChatRequestUserMessageContentInChatMessageContentItemArray() { ChatRequestUserMessage userMessage = new ChatRequestUserMessage(Arrays.asList( - new ChatMessageTextContentItem("textContent"), - new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) + new ChatMessageTextContentItem("textContent"), + new ChatMessageImageContentItem(new ChatMessageImageUrl("testImage")) )); assertChatRequestUserMessage(userMessage); } @@ -49,35 +64,80 @@ public void testChatRequestUserMessageContentInChatMessageContentItemArray() { public void testChatRequestSystemMessage() { ChatRequestSystemMessage chatRequestSystemMessage = new ChatRequestSystemMessage(content).setName(name); assertEquals(content, chatRequestSystemMessage.getContent()); + + // Test it in ChatCompletionsOptions + ChatCompletionsOptions chatCompletionsOptions = new ChatCompletionsOptions(Arrays.asList(chatRequestSystemMessage)); + assertChatCompletionsOptions(chatCompletionsOptions); } @Test public void testChatRequestAssistantMessage() { ChatRequestAssistantMessage chatRequestAssistantMessage = new ChatRequestAssistantMessage(content).setName(name); assertEquals(content, chatRequestAssistantMessage.getContent()); + + // Test it in ChatCompletionsOptions + ChatCompletionsOptions chatCompletionsOptions = new ChatCompletionsOptions(Arrays.asList(chatRequestAssistantMessage)); + assertChatCompletionsOptions(chatCompletionsOptions); } @Test public void testChatRequestToolMessage() { ChatRequestToolMessage chatRequestToolMessage = new ChatRequestToolMessage(content, "tool_call_id_value"); assertEquals(content, chatRequestToolMessage.getContent()); + + // Test it in ChatCompletionsOptions + ChatCompletionsOptions chatCompletionsOptions = new ChatCompletionsOptions(Arrays.asList(chatRequestToolMessage)); + assertChatCompletionsOptions(chatCompletionsOptions); } @Test public void testChatRequestFunctionMessage() { ChatRequestFunctionMessage chatRequestFunctionMessage = new ChatRequestFunctionMessage(name, content); assertEquals(content, chatRequestFunctionMessage.getContent()); + + // Test it in ChatCompletionsOptions + ChatCompletionsOptions chatCompletionsOptions = new ChatCompletionsOptions(Arrays.asList(chatRequestFunctionMessage)); + assertChatCompletionsOptions(chatCompletionsOptions); } private void assertChatRequestUserMessage(ChatRequestUserMessage userMessage) { String userMessageInString = BinaryData.fromObject(userMessage).toString(); - assertEquals( - "{\"content\":[{\"text\":\"textContent\",\"type\":\"text\"},{\"image_url\":{\"url\":\"testImage\"},\"type\":\"image_url\"}],\"role\":\"user\"}", - userMessageInString); ChatRequestUserMessage converted = BinaryData.fromString(userMessageInString) .toObject(ChatRequestUserMessage.class); assertEquals(userMessage.getContent().toString(), converted.getContent().toString()); assertEquals(userMessage.getName(), converted.getName()); assertEquals(USER, converted.getRole()); } + + private void assertChatCompletionsOptions(ChatCompletionsOptions chatCompletionsOptions) { + String chatCompletionsOptionsInString = BinaryData.fromObject(chatCompletionsOptions).toString(); + ChatCompletionsOptions converted = BinaryData.fromString(chatCompletionsOptionsInString) + .toObject(ChatCompletionsOptions.class); + + converted.getMessages().forEach(message -> { + if (message instanceof ChatRequestUserMessage) { + ChatRequestUserMessage userMessage = (ChatRequestUserMessage) message; + ChatRequestUserMessage expectedUserMessage = (ChatRequestUserMessage) chatCompletionsOptions.getMessages().get(0); + assertEquals(expectedUserMessage.getContent().toString(), userMessage.getContent().toString()); + } else if (message instanceof ChatRequestSystemMessage) { + ChatRequestSystemMessage systemMessage = (ChatRequestSystemMessage) message; + ChatRequestSystemMessage expectedSystemMessage = (ChatRequestSystemMessage) chatCompletionsOptions.getMessages().get(0); + assertEquals(expectedSystemMessage.getContent(), systemMessage.getContent()); + } else if (message instanceof ChatRequestAssistantMessage) { + ChatRequestAssistantMessage assistantMessage = (ChatRequestAssistantMessage) message; + ChatRequestAssistantMessage expectedAssistantMessage = (ChatRequestAssistantMessage) chatCompletionsOptions.getMessages().get(0); + assertEquals(expectedAssistantMessage.getContent(), assistantMessage.getContent()); + } else if (message instanceof ChatRequestToolMessage) { + ChatRequestToolMessage toolMessage = (ChatRequestToolMessage) message; + ChatRequestToolMessage expectedToolMessage = (ChatRequestToolMessage) chatCompletionsOptions.getMessages().get(0); + assertEquals(expectedToolMessage.getContent(), toolMessage.getContent()); + } else if (message instanceof ChatRequestFunctionMessage) { + ChatRequestFunctionMessage functionMessage = (ChatRequestFunctionMessage) message; + ChatRequestFunctionMessage expectedFunctionMessage = (ChatRequestFunctionMessage) chatCompletionsOptions.getMessages().get(0); + assertEquals(expectedFunctionMessage.getContent(), functionMessage.getContent()); + } else { + assertFalse(true, "Unexpected message type"); + } + }); + } } From 30e552f990a1a202e73ae7a7783be18f27324b8b Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Tue, 25 Jun 2024 15:46:04 -0700 Subject: [PATCH 07/11] address feedbacks --- eng/versioning/version_client.txt | 1 + sdk/openai/azure-ai-openai/pom.xml | 2 +- .../ai/openai/models/ChatRequestUserMessage.java | 15 ++++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 70e3af4b21bb..ccf565f82446 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -479,6 +479,7 @@ io.clientcore:http-stress;1.0.0-beta.1;1.0.0-beta.1 # note: The unreleased dependencies will not be manipulated with the automatic PR creation code. # In the pom, the version update tag after the version should name the unreleased package and the dependency version: # +unreleased_com.azure:azure-core;1.50.0-beta.1 # Released Beta dependencies: Copy the entry from above, prepend "beta_", remove the current # version and set the version to the released beta. Released beta dependencies are only valid diff --git a/sdk/openai/azure-ai-openai/pom.xml b/sdk/openai/azure-ai-openai/pom.xml index 336b554036bc..a59035859ce9 100644 --- a/sdk/openai/azure-ai-openai/pom.xml +++ b/sdk/openai/azure-ai-openai/pom.xml @@ -60,7 +60,7 @@ com.azure azure-core - 1.49.1 + 1.50.0-beta.1 com.azure diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index 13f98ebb41e5..09f5517fa57d 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -9,7 +9,6 @@ import com.azure.json.JsonReader; import com.azure.json.JsonToken; import com.azure.json.JsonWriter; - import java.io.IOException; import java.util.List; @@ -122,7 +121,7 @@ public ChatRole getRole() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeBinaryField("content", this.content.toBytes()); + jsonWriter.writeUntypedField("content", this.content); jsonWriter.writeStringField("role", this.role == null ? null : this.role.toString()); jsonWriter.writeStringField("name", this.name); return jsonWriter.writeEndObject(); @@ -146,7 +145,17 @@ public static ChatRequestUserMessage fromJson(JsonReader jsonReader) throws IOEx String fieldName = reader.getFieldName(); reader.nextToken(); if ("content".equals(fieldName)) { - content = BinaryData.fromBytes(reader.getBinary()); + if (reader.currentToken() == JsonToken.STRING) { + content = BinaryData.fromString(reader.getString()); + } else if (reader.currentToken() == JsonToken.START_OBJECT) { + content = BinaryData.fromObject(reader.readMap(JsonReader::readUntyped)); + } else if (reader.currentToken() == JsonToken.START_ARRAY) { + content = BinaryData.fromObject(reader.readArray(JsonReader::readUntyped)); + } else if (reader.currentToken() == JsonToken.NULL) { + content = null; + } else { + throw new IllegalStateException("Unexpected token: " + reader.currentToken()); + } } else if ("role".equals(fieldName)) { role = ChatRole.fromString(reader.getString()); } else if ("name".equals(fieldName)) { From 28fc55256ad9b662afe7d21a967bc38b057844bb Mon Sep 17 00:00:00 2001 From: Shawn Fang Date: Wed, 26 Jun 2024 13:04:27 -0700 Subject: [PATCH 08/11] Enhance error message --- .../com/azure/ai/openai/models/ChatRequestUserMessage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index 09f5517fa57d..e9c66f9d5d58 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -154,7 +154,8 @@ public static ChatRequestUserMessage fromJson(JsonReader jsonReader) throws IOEx } else if (reader.currentToken() == JsonToken.NULL) { content = null; } else { - throw new IllegalStateException("Unexpected token: " + reader.currentToken()); + throw new IllegalStateException("Unexpected 'content' type found when deserializing" + + " ChatRequestUserMessage JSON object: " + reader.currentToken()); } } else if ("role".equals(fieldName)) { role = ChatRole.fromString(reader.getString()); From f0ea75b4c8c28025602c3a941e099b7cebaee95f Mon Sep 17 00:00:00 2001 From: Srikanta Nagaraja Date: Fri, 28 Jun 2024 12:39:20 -0700 Subject: [PATCH 09/11] BinaryData serialization and deserialization --- .../core/implementation/util/SerializableContent.java | 10 +++++++++- .../src/main/java/com/azure/core/util/BinaryData.java | 4 ++++ sdk/core/azure-core/src/main/java/module-info.java | 5 +++-- .../main/java/com/azure/ai/openai/OpenAIClient.java | 10 ++++------ .../azure/ai/openai/models/ChatRequestUserMessage.java | 7 +++---- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java index c34ba9fc2c75..e52684d2acdf 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java @@ -8,6 +8,8 @@ import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.serializer.ObjectSerializer; import com.azure.core.util.serializer.TypeReference; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonWriter; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -27,7 +29,7 @@ /** * A {@link BinaryDataContent} implementation which is backed by a serializable object. */ -public final class SerializableContent extends BinaryDataContent { +public final class SerializableContent extends BinaryDataContent implements JsonSerializable { private static final ClientLogger LOGGER = new ClientLogger(SerializableContent.class); private final Object content; @@ -129,4 +131,10 @@ public BinaryDataContentType getContentType() { private byte[] getBytes() { return serializer.serializeToBytes(content); } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeRawValue(new String(this.serializer.serializeToBytes(this.content))); + return jsonWriter; + } } diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java b/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java index 3010516327a0..b2c1fa7302e5 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java @@ -1608,4 +1608,8 @@ public Mono toReplayableBinaryDataAsync() { return content.toReplayableContentAsync().map(BinaryData::new); } } + + public BinaryDataContent getContent() { + return this.content; + } } diff --git a/sdk/core/azure-core/src/main/java/module-info.java b/sdk/core/azure-core/src/main/java/module-info.java index 7211d1160cb3..eb4794519858 100644 --- a/sdk/core/azure-core/src/main/java/module-info.java +++ b/sdk/core/azure-core/src/main/java/module-info.java @@ -44,8 +44,9 @@ exports com.azure.core.implementation.jackson to com.azure.core.management, com.azure.core.serializer.json.jackson; // export core utilities to other core packages. - exports com.azure.core.implementation.util to com.azure.http.netty, com.azure.core.http.okhttp, - com.azure.core.http.jdk.httpclient, com.azure.core.http.vertx, com.azure.core.serializer.json.jackson; + exports com.azure.core.implementation.util + to com.azure.http.netty, com.azure.core.http.okhttp, com.azure.core.http.jdk.httpclient, + com.azure.core.http.vertx, com.azure.core.serializer.json.jackson, com.azure.ai.openai; exports com.azure.core.util.polling.implementation to com.azure.core.experimental; // exporting some packages specifically for Jackson diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java index 51a1b43a67cb..c21c17f7c91b 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java @@ -52,8 +52,6 @@ @ServiceClient(builder = OpenAIClientBuilder.class) public final class OpenAIClient { - private static final ClientLogger LOGGER = new ClientLogger(OpenAIClient.class); - @Generated private final OpenAIClientImpl serviceClient; @@ -1056,7 +1054,7 @@ public Response getAudioTranslationTextWithResponse(String deploymentOrM * Gets transcribed text and associated metadata from provided spoken audio data. Audio will be transcribed in the * written language corresponding to the language it was spoken in. *

Response Body Schema

- * + * *
{@code
      * {
      *     text: String (Required)
@@ -1113,7 +1111,7 @@ Response getAudioTranscriptionAsResponseObjectWithResponse(String de
      * Gets transcribed text and associated metadata from provided spoken audio data. Audio will be transcribed in the
      * written language corresponding to the language it was spoken in.
      * 

Response Body Schema

- * + * *
{@code
      * String
      * }
@@ -1141,7 +1139,7 @@ Response getAudioTranscriptionAsPlainTextWithResponse(String deploym /** * Gets English language transcribed text and associated metadata from provided spoken audio data. *

Response Body Schema

- * + * *
{@code
      * {
      *     text: String (Required)
@@ -1191,7 +1189,7 @@ Response getAudioTranslationAsResponseObjectWithResponse(String depl
     /**
      * Gets English language transcribed text and associated metadata from provided spoken audio data.
      * 

Response Body Schema

- * + * *
{@code
      * String
      * }
diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index e9c66f9d5d58..34334c90f12c 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -121,7 +121,7 @@ public ChatRole getRole() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeUntypedField("content", this.content); + jsonWriter.writeUntypedField("content", this.content.getContent()); jsonWriter.writeStringField("role", this.role == null ? null : this.role.toString()); jsonWriter.writeStringField("name", this.name); return jsonWriter.writeEndObject(); @@ -147,10 +147,9 @@ public static ChatRequestUserMessage fromJson(JsonReader jsonReader) throws IOEx if ("content".equals(fieldName)) { if (reader.currentToken() == JsonToken.STRING) { content = BinaryData.fromString(reader.getString()); - } else if (reader.currentToken() == JsonToken.START_OBJECT) { - content = BinaryData.fromObject(reader.readMap(JsonReader::readUntyped)); } else if (reader.currentToken() == JsonToken.START_ARRAY) { - content = BinaryData.fromObject(reader.readArray(JsonReader::readUntyped)); + content = BinaryData.fromObject(reader.readArray(arrayReader -> arrayReader + .readObject(ChatMessageContentItem::fromJson))); } else if (reader.currentToken() == JsonToken.NULL) { content = null; } else { From 0d551b64b51259911380b9183263dfcfa1a90aea Mon Sep 17 00:00:00 2001 From: Srikanta Nagaraja Date: Fri, 28 Jun 2024 13:31:30 -0700 Subject: [PATCH 10/11] Fix chat request user message serialization --- .../util/SerializableContent.java | 10 +------- .../java/com/azure/core/util/BinaryData.java | 3 --- .../azure-core/src/main/java/module-info.java | 2 +- .../openai/models/ChatRequestUserMessage.java | 24 +++++++++++++++---- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java index e52684d2acdf..c34ba9fc2c75 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/util/SerializableContent.java @@ -8,8 +8,6 @@ import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.serializer.ObjectSerializer; import com.azure.core.util.serializer.TypeReference; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonWriter; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -29,7 +27,7 @@ /** * A {@link BinaryDataContent} implementation which is backed by a serializable object. */ -public final class SerializableContent extends BinaryDataContent implements JsonSerializable { +public final class SerializableContent extends BinaryDataContent { private static final ClientLogger LOGGER = new ClientLogger(SerializableContent.class); private final Object content; @@ -131,10 +129,4 @@ public BinaryDataContentType getContentType() { private byte[] getBytes() { return serializer.serializeToBytes(content); } - - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeRawValue(new String(this.serializer.serializeToBytes(this.content))); - return jsonWriter; - } } diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java b/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java index b2c1fa7302e5..f1b3aa1c3135 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/util/BinaryData.java @@ -1609,7 +1609,4 @@ public Mono toReplayableBinaryDataAsync() { } } - public BinaryDataContent getContent() { - return this.content; - } } diff --git a/sdk/core/azure-core/src/main/java/module-info.java b/sdk/core/azure-core/src/main/java/module-info.java index eb4794519858..4e0b2b51d73c 100644 --- a/sdk/core/azure-core/src/main/java/module-info.java +++ b/sdk/core/azure-core/src/main/java/module-info.java @@ -46,7 +46,7 @@ // export core utilities to other core packages. exports com.azure.core.implementation.util to com.azure.http.netty, com.azure.core.http.okhttp, com.azure.core.http.jdk.httpclient, - com.azure.core.http.vertx, com.azure.core.serializer.json.jackson, com.azure.ai.openai; + com.azure.core.http.vertx, com.azure.core.serializer.json.jackson; exports com.azure.core.util.polling.implementation to com.azure.core.experimental; // exporting some packages specifically for Jackson diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index 34334c90f12c..b83d6f0025ce 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -10,6 +10,7 @@ import com.azure.json.JsonToken; import com.azure.json.JsonWriter; import java.io.IOException; +import java.util.Arrays; import java.util.List; /** @@ -23,6 +24,8 @@ public final class ChatRequestUserMessage extends ChatRequestMessage { */ @Generated private final BinaryData content; + private final String stringContent; + private final List chatMessageContentItems; /* * An optional name for the participant. @@ -37,6 +40,8 @@ public final class ChatRequestUserMessage extends ChatRequestMessage { */ private ChatRequestUserMessage(BinaryData content) { this.content = content; + this.chatMessageContentItems = null; + this.stringContent = null; } /** @@ -45,7 +50,9 @@ private ChatRequestUserMessage(BinaryData content) { * @param content The plain text content associated with the message. */ public ChatRequestUserMessage(String content) { - this(BinaryData.fromString(content)); + this.content = BinaryData.fromString(content); + this.stringContent = content; + this.chatMessageContentItems = null; } /** @@ -54,7 +61,9 @@ public ChatRequestUserMessage(String content) { * @param content The collection of structured content associated with the message. */ public ChatRequestUserMessage(ChatMessageContentItem[] content) { - this(BinaryData.fromObject(content)); + this.content = BinaryData.fromObject(content); + this.chatMessageContentItems = Arrays.asList(content); + this.stringContent = null; } /** @@ -63,7 +72,9 @@ public ChatRequestUserMessage(ChatMessageContentItem[] content) { * @param content The collection of structured content associated with the message. */ public ChatRequestUserMessage(List content) { - this(BinaryData.fromObject(content)); + this.content = BinaryData.fromObject(content); + this.stringContent = null; + this.chatMessageContentItems = content; } /** @@ -121,7 +132,12 @@ public ChatRole getRole() { @Override public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); - jsonWriter.writeUntypedField("content", this.content.getContent()); + if (stringContent != null) { + jsonWriter.writeStringField("content", stringContent); + } else if(chatMessageContentItems != null){ + jsonWriter.writeArrayField("content", chatMessageContentItems, JsonWriter::writeJson); + } + jsonWriter.writeStringField("role", this.role == null ? null : this.role.toString()); jsonWriter.writeStringField("name", this.name); return jsonWriter.writeEndObject(); From 654b9269eed9957b2eb4706a266dc7633ad0a859 Mon Sep 17 00:00:00 2001 From: Srikanta Nagaraja Date: Fri, 28 Jun 2024 13:35:34 -0700 Subject: [PATCH 11/11] Fix checkstyle issues --- .../src/main/java/com/azure/ai/openai/OpenAIClient.java | 1 - .../java/com/azure/ai/openai/models/ChatRequestUserMessage.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java index c21c17f7c91b..9cd96db16a1c 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/OpenAIClient.java @@ -29,7 +29,6 @@ import com.azure.core.http.rest.Response; import com.azure.core.http.rest.SimpleResponse; import com.azure.core.util.BinaryData; -import com.azure.core.util.logging.ClientLogger; import com.azure.ai.openai.implementation.CompletionsUtils; import com.azure.ai.openai.implementation.MultipartDataHelper; import com.azure.ai.openai.implementation.MultipartDataSerializationResult; diff --git a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java index b83d6f0025ce..24319f5b91c5 100644 --- a/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java +++ b/sdk/openai/azure-ai-openai/src/main/java/com/azure/ai/openai/models/ChatRequestUserMessage.java @@ -134,7 +134,7 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStartObject(); if (stringContent != null) { jsonWriter.writeStringField("content", stringContent); - } else if(chatMessageContentItems != null){ + } else if (chatMessageContentItems != null) { jsonWriter.writeArrayField("content", chatMessageContentItems, JsonWriter::writeJson); }