Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/openai/azure-ai-openai/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/openai/azure-ai-openai",
"Tag": "java/openai/azure-ai-openai_6a5c74f82e"
"Tag": "java/openai/azure-ai-openai_ab2dde7d23"
}
5 changes: 5 additions & 0 deletions sdk/openai/azure-ai-openai/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@
<artifactId>azure-core-http-netty</artifactId>
<version>1.13.4</version> <!-- {x-version-update;com.azure:azure-core-http-netty;dependency} -->
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-experimental</artifactId>
<version>1.0.0-beta.40</version> <!-- {x-version-update;com.azure:azure-core-experimental;dependency} -->
</dependency>

<!-- provided scope -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import com.azure.ai.openai.models.CompletionsOptions;
import com.azure.ai.openai.models.Embeddings;
import com.azure.ai.openai.models.EmbeddingsOptions;
import com.azure.ai.openai.models.ImageGenerationOptions;
import com.azure.ai.openai.models.ImageOperationResponse;
import com.azure.ai.openai.models.ImageOperationStatus;
import com.azure.core.annotation.Generated;
import com.azure.core.annotation.ReturnType;
import com.azure.core.annotation.ServiceClient;
Expand All @@ -21,10 +24,12 @@
import com.azure.core.exception.HttpResponseException;
import com.azure.core.exception.ResourceModifiedException;
import com.azure.core.exception.ResourceNotFoundException;
import com.azure.core.experimental.models.PollResult;
import com.azure.core.http.rest.RequestOptions;
import com.azure.core.http.rest.Response;
import com.azure.core.util.BinaryData;
import com.azure.core.util.FluxUtil;
import com.azure.core.util.polling.PollerFlux;
import java.nio.ByteBuffer;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand Down Expand Up @@ -446,4 +451,149 @@ public Flux<ChatCompletions> getChatCompletionsStream(
this.serviceClient = null;
openAIServiceClient = serviceClient;
}

/**
* Returns the status of the images operation.
*
* <p><strong>Response Body Schema</strong>
*
* <pre>{@code
* {
* id: String (Required)
* created: long (Required)
* expires: Long (Optional)
* result (Optional): {
* created: long (Required)
* data (Required): [
* (Required){
* url: String (Optional)
* error (Optional): {
* code: String (Required)
* message: String (Required)
* target: String (Optional)
* details (Optional): [
* (recursive schema, see above)
* ]
* innererror (Optional): {
* code: String (Optional)
* innererror (Optional): (recursive schema, see innererror above)
* }
* }
* }
* ]
* }
* status: String(notRunning/running/succeeded/canceled/failed/deleted) (Required)
* error (Optional): (recursive schema, see error above)
* }
* }</pre>
*
* @param operationId .
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @return the result of the operation if the operation succeeded along with {@link Response} on successful
* completion of {@link Mono}.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono<Response<BinaryData>> getImageOperationStatusWithResponse(
String operationId, RequestOptions requestOptions) {
return this.serviceClient.getImageOperationStatusWithResponseAsync(operationId, requestOptions);
}

/**
* Starts the generation of a batch of images from a text caption.
*
* <p><strong>Request Body Schema</strong>
*
* <pre>{@code
* {
* prompt: String (Required)
* n: Integer (Optional)
* size: String(256x256/512x512/1024x1024) (Optional)
* user: String (Optional)
* }
* }</pre>
*
* <p><strong>Response Body Schema</strong>
*
* <pre>{@code
* {
* id: String (Required)
* status: String (Required)
* error (Optional): {
* code: String (Required)
* message: String (Required)
* target: String (Optional)
* details (Optional): [
* (recursive schema, see above)
* ]
* innererror (Optional): {
* code: String (Optional)
* innererror (Optional): (recursive schema, see innererror above)
* }
* }
* }
* }</pre>
*
* @param imageGenerationOptions Represents the request data used to generate images.
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @return the {@link PollerFlux} for polling of status details for long running operations.
*/
@Generated
@ServiceMethod(returns = ReturnType.LONG_RUNNING_OPERATION)
public PollerFlux<BinaryData, BinaryData> beginStartGenerateImage(
BinaryData imageGenerationOptions, RequestOptions requestOptions) {
return this.serviceClient.beginStartGenerateImageAsync(imageGenerationOptions, requestOptions);
}

/**
* Returns the status of the images operation.
*
* @param operationId .
* @throws IllegalArgumentException thrown if parameters fail the validation.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
* @return the result of the operation if the operation succeeded on successful completion of {@link Mono}.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono<ImageOperationResponse> getImageOperationStatus(String operationId) {
// Generated convenience method for getImageOperationStatusWithResponse
RequestOptions requestOptions = new RequestOptions();
return getImageOperationStatusWithResponse(operationId, requestOptions)
.flatMap(FluxUtil::toMono)
.map(protocolMethodData -> protocolMethodData.toObject(ImageOperationResponse.class));
}

/**
* Starts the generation of a batch of images from a text caption.
*
* @param imageGenerationOptions Represents the request data used to generate images.
* @throws IllegalArgumentException thrown if parameters fail the validation.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
* @return the {@link PollerFlux} for polling of status details for long running operations.
*/
@Generated
@ServiceMethod(returns = ReturnType.LONG_RUNNING_OPERATION)
public PollerFlux<PollResult, ImageOperationStatus> beginStartGenerateImage(
ImageGenerationOptions imageGenerationOptions) {
// Generated convenience method for beginStartGenerateImageWithModel
RequestOptions requestOptions = new RequestOptions();
return serviceClient.beginStartGenerateImageWithModelAsync(
BinaryData.fromObject(imageGenerationOptions), requestOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import com.azure.ai.openai.models.CompletionsOptions;
import com.azure.ai.openai.models.Embeddings;
import com.azure.ai.openai.models.EmbeddingsOptions;
import com.azure.ai.openai.models.ImageGenerationOptions;
import com.azure.ai.openai.models.ImageOperationResponse;
import com.azure.ai.openai.models.ImageOperationStatus;
import com.azure.core.annotation.Generated;
import com.azure.core.annotation.ReturnType;
import com.azure.core.annotation.ServiceClient;
Expand All @@ -21,10 +24,12 @@
import com.azure.core.exception.HttpResponseException;
import com.azure.core.exception.ResourceModifiedException;
import com.azure.core.exception.ResourceNotFoundException;
import com.azure.core.experimental.models.PollResult;
import com.azure.core.http.rest.RequestOptions;
import com.azure.core.http.rest.Response;
import com.azure.core.util.BinaryData;
import com.azure.core.util.IterableStream;
import com.azure.core.util.polling.SyncPoller;
import java.nio.ByteBuffer;
import reactor.core.publisher.Flux;

Expand Down Expand Up @@ -438,4 +443,147 @@ public IterableStream<ChatCompletions> getChatCompletionsStream(
this.serviceClient = null;
openAIServiceClient = serviceClient;
}

/**
* Returns the status of the images operation.
*
* <p><strong>Response Body Schema</strong>
*
* <pre>{@code
* {
* id: String (Required)
* created: long (Required)
* expires: Long (Optional)
* result (Optional): {
* created: long (Required)
* data (Required): [
* (Required){
* url: String (Optional)
* error (Optional): {
* code: String (Required)
* message: String (Required)
* target: String (Optional)
* details (Optional): [
* (recursive schema, see above)
* ]
* innererror (Optional): {
* code: String (Optional)
* innererror (Optional): (recursive schema, see innererror above)
* }
* }
* }
* ]
* }
* status: String(notRunning/running/succeeded/canceled/failed/deleted) (Required)
* error (Optional): (recursive schema, see error above)
* }
* }</pre>
*
* @param operationId .
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @return the result of the operation if the operation succeeded along with {@link Response}.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
public Response<BinaryData> getImageOperationStatusWithResponse(String operationId, RequestOptions requestOptions) {
return this.serviceClient.getImageOperationStatusWithResponse(operationId, requestOptions);
}

/**
* Starts the generation of a batch of images from a text caption.
*
* <p><strong>Request Body Schema</strong>
*
* <pre>{@code
* {
* prompt: String (Required)
* n: Integer (Optional)
* size: String(256x256/512x512/1024x1024) (Optional)
* user: String (Optional)
* }
* }</pre>
*
* <p><strong>Response Body Schema</strong>
*
* <pre>{@code
* {
* id: String (Required)
* status: String (Required)
* error (Optional): {
* code: String (Required)
* message: String (Required)
* target: String (Optional)
* details (Optional): [
* (recursive schema, see above)
* ]
* innererror (Optional): {
* code: String (Optional)
* innererror (Optional): (recursive schema, see innererror above)
* }
* }
* }
* }</pre>
*
* @param imageGenerationOptions Represents the request data used to generate images.
* @param requestOptions The options to configure the HTTP request before HTTP client sends it.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @return the {@link SyncPoller} for polling of status details for long running operations.
*/
@Generated
@ServiceMethod(returns = ReturnType.LONG_RUNNING_OPERATION)
public SyncPoller<BinaryData, BinaryData> beginStartGenerateImage(
BinaryData imageGenerationOptions, RequestOptions requestOptions) {
return this.serviceClient.beginStartGenerateImage(imageGenerationOptions, requestOptions);
}

/**
* Returns the status of the images operation.
*
* @param operationId .
* @throws IllegalArgumentException thrown if parameters fail the validation.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
* @return the result of the operation if the operation succeeded.
*/
@Generated
@ServiceMethod(returns = ReturnType.SINGLE)
public ImageOperationResponse getImageOperationStatus(String operationId) {
// Generated convenience method for getImageOperationStatusWithResponse
RequestOptions requestOptions = new RequestOptions();
return getImageOperationStatusWithResponse(operationId, requestOptions)
.getValue()
.toObject(ImageOperationResponse.class);
}

/**
* Starts the generation of a batch of images from a text caption.
*
* @param imageGenerationOptions Represents the request data used to generate images.
* @throws IllegalArgumentException thrown if parameters fail the validation.
* @throws HttpResponseException thrown if the request is rejected by server.
* @throws ClientAuthenticationException thrown if the request is rejected by server on status code 401.
* @throws ResourceNotFoundException thrown if the request is rejected by server on status code 404.
* @throws ResourceModifiedException thrown if the request is rejected by server on status code 409.
* @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent.
* @return the {@link SyncPoller} for polling of status details for long running operations.
*/
@Generated
@ServiceMethod(returns = ReturnType.LONG_RUNNING_OPERATION)
public SyncPoller<PollResult, ImageOperationStatus> beginStartGenerateImage(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we consider to rename this method name.

"beginStart" sounds the same thing.

Might be "beginGenerateImage" since it is LRO and should have started with begin prefix.

Copy link
Member Author

@jpalvarezl jpalvarezl Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a downstream change from the TSP definition. Let's surface this feedback in the Wednesday meeting, if that works for you.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jpalvarezl

There is something about the definition of this LRO in tsp that Java codegen being checking with author.
Azure/azure-rest-api-specs#24382 (comment)

And arch is checking on the terminal state.
https://github.com/Azure/azure-rest-api-specs/pull/24382/files#r1237679552

So, the LRO may not work as expected in runtime. If you do test it, please let me know the runtime log. Thanks in advance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. I've made a work item around this. It might be a couple of days before we get to the actual unit testing of this. Will keep you posted.

Copy link
Member

@weidongxu-microsoft weidongxu-microsoft Jun 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Srikanta did a test

The final result is

{
  "created": 1687461794,
  "expires": 1687548201,
  "id": "bc67228d-12ec-45c7-b73a-3b8486465dfe",
  "result": {
    "created": 1687461794,
    "data": [
      {
        "url": "###"
      },
...
    ]
  },
  "status": "succeeded"
}

So the tsp definition on the LRO is wrong.

Copy link
Member

@weidongxu-microsoft weidongxu-microsoft Jun 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A related topic: we also need some automation that runs when we update the autorest-java and typespec-java, to re-generate the SDK that has a codegen CI include, so that the upgrade on codegen won't break these CI.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am currently experiencing issues with the LRO in a sync scenario. For some reason I get 2 consecutive messages with status notRunning which prevent the continuation of the request. Async works as expected:

Sync call JSON:

{
  "Entries": [
    {
      "RequestUri": "https://REDACTED/openai/images/generations:submit?api-version=2023-06-01-preview",
      "RequestMethod": "POST",
      "RequestHeaders": {
       // ...
      },
      "RequestBody": {
        "prompt": "A drawing of the Seattle skyline in the style of Van Gogh"
      },
      "StatusCode": 202,
      "ResponseHeaders": {
        // ...
      },
      "ResponseBody": {
        "id": "22ee7ca1-2ff3-4698-8ff9-94fb8e4e3c32",
        "status": "notRunning"
      }
    },
    {
      "RequestUri": "https://REDACTED/openai/operations/images/22ee7ca1-2ff3-4698-8ff9-94fb8e4e3c32?api-version=2023-06-01-preview",
      "RequestMethod": "GET",
      "RequestHeaders": {
        // ...
      },
      "RequestBody": null,
      "StatusCode": 200,
      "ResponseHeaders": {
        // ...
      },
      "ResponseBody": {
        "created": 1687797150,
        "id": "22ee7ca1-2ff3-4698-8ff9-94fb8e4e3c32",
        "status": "notRunning"
      }
    }
  ],
  "Variables": {}
}

The Async case:

{
  "Entries": [
    {
      "RequestUri": "https://REDACTED/openai/images/generations:submit?api-version=2023-06-01-preview",
      "RequestMethod": "POST",
      "RequestHeaders": {
        // ...
      },
      "RequestBody": {
        "prompt": "A drawing of the Seattle skyline in the style of Van Gogh"
      },
      "StatusCode": 202,
      "ResponseHeaders": {
        // ...
      },
      "ResponseBody": {
        "id": "92da53f9-7d06-4d89-8a90-8667757f49b9",
        "status": "notRunning"
      }
    },
    {
      "RequestUri": "https://REDACTED/openai/operations/images/92da53f9-7d06-4d89-8a90-8667757f49b9?api-version=2023-06-01-preview",
      "RequestMethod": "GET",
      "RequestHeaders": {
        // ...
      },
      "RequestBody": null,
      "StatusCode": 200,
      "ResponseHeaders": {
        // ...
      },
      "ResponseBody": {
        "created": 1687796853,
        "id": "92da53f9-7d06-4d89-8a90-8667757f49b9",
        "status": "running"
      }
    },
    {
      "RequestUri": "https://REDACTED/openai/operations/images/92da53f9-7d06-4d89-8a90-8667757f49b9?api-version=2023-06-01-preview",
      "RequestMethod": "GET",
      "RequestHeaders": {
        // ...
      },
      "RequestBody": null,
      "StatusCode": 200,
      "ResponseHeaders": {
        // ...
      },
      "ResponseBody": {
        "created": 1687796853,
        "id": "92da53f9-7d06-4d89-8a90-8667757f49b9",
        "status": "running"
      }
    },
    {
      "RequestUri": "https://REDACTED/openai/operations/images/92da53f9-7d06-4d89-8a90-8667757f49b9?api-version=2023-06-01-preview",
      "RequestMethod": "GET",
      "RequestHeaders": {
        // ...
      },
      "RequestBody": null,
      "StatusCode": 200,
      "ResponseHeaders": {
        // ...
      },
      "ResponseBody": {
        "created": 1687796853,
        "expires": 1687883259,
        "id": "92da53f9-7d06-4d89-8a90-8667757f49b9",
        "result": {
          "created": 1687796853,
          "data": [
            {
              "url": "REDACTED"
            }
          ]
        },
        "status": "succeeded"
      }
    }
  ],
  "Variables": {}
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you can see in the sync scenario we never actually get to a running state. Could be an issue with the local setup. Here is the code for Async:

public Mono<ImageOperationResponse> generateImage(ImageGenerationOptions imageGenerationOptions) {
  RequestOptions requestOptions = new RequestOptions();
  BinaryData imageGenerationOptionsBinaryData = BinaryData.fromObject(imageGenerationOptions);
  return serviceClient.beginStartGenerateImageAsync(imageGenerationOptions, requestOptions)
                .last()
                .flatMap(it -> it.getFinalResult())
                .map(it -> it.toObject(ImageOperationResponse.class));
}

And for the sync version. (the commented out options exhibit the same behaviour.

RequestOptions requestOptions = new RequestOptions();
        return beginStartGenerateImage(BinaryData.fromObject(imageGenerationOptions), requestOptions)
// option 1
            .waitUntil(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED).getValue()
// option 2
//            .getFinalResult()

// option 3
//            .waitForCompletion()
//            .getValue()
            .toObject(ImageOperationResponse.class);

Copy link
Member

@weidongxu-microsoft weidongxu-microsoft Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't aware that we had sync-stack enabled in openai. There were still a bug in pageable in current 1.40.0 core (that would be fixed in next 1.41.0 core).

OK, good thing is that openai don't have a pageable :-)

Copy link
Member

@weidongxu-microsoft weidongxu-microsoft Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the problem with sync polling, I am suspicious of a possible bug in SimpleSyncPoller. Investigating.
Issue #35628. It seems current code would treat any unknown status as isComplete=true.

@samvaity saw one in purview as well. Maybe same cause.

ImageGenerationOptions imageGenerationOptions) {
// Generated convenience method for beginStartGenerateImageWithModel
RequestOptions requestOptions = new RequestOptions();
return serviceClient.beginStartGenerateImageWithModel(
BinaryData.fromObject(imageGenerationOptions), requestOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ public enum OpenAIServiceVersion implements ServiceVersion {
/** Enum value 2022-12-01. */
V2022_12_01("2022-12-01"),

/** Enum value 2023-03-15-preview. */
V2023_03_15_PREVIEW("2023-03-15-preview");
/** Enum value 2023-05-15. */
V2023_05_15("2023-05-15"),

/** Enum value 2023-06-01-preview. */
V2023_06_01_PREVIEW("2023-06-01-preview");

private final String version;

Expand All @@ -32,6 +35,6 @@ public String getVersion() {
* @return The latest {@link OpenAIServiceVersion}.
*/
public static OpenAIServiceVersion getLatest() {
return V2023_03_15_PREVIEW;
return V2023_06_01_PREVIEW;
}
}
Loading