# Go

These settings apply only when `--go` is specified on the command line.

``` yaml
input-file:
- https://github.com/Azure/azure-rest-api-specs/blob/3e0e2a93ddb3c9c44ff1baf4952baa24ca98e9db/specification/cognitiveservices/data-plane/AzureOpenAI/inference/preview/2023-12-01-preview/generated.json
output-folder: ../azopenai
clear-output-folder: false
module: github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai
license-header: MICROSOFT_MIT_NO_VERSION
openapi-type: data-plane
go: true
use: "@autorest/go@4.0.0-preview.52"
title: "OpenAI"
slice-elements-byval: true
# can't use this since it removes an innererror type that we want ()
# remove-non-reference-schema: true
```

## Transformations

Fix deployment and endpoint parameters so they show up in the right spots

``` yaml
directive:
  # Add x-ms-parameter-location to parameters in x-ms-parameterized-host
  - from: swagger-document
    where: $["x-ms-parameterized-host"].parameters.0
    transform: $["x-ms-parameter-location"] = "client";
  # Make deploymentId a client parameter
  # This must be done in each operation as the parameter is not defined in the components section
  - from: swagger-document
    where: $.paths..parameters..[?(@.name=='deploymentId')]
    transform: $["x-ms-parameter-location"] = "client";
```

## Model -> DeploymentName

```yaml
directive:
  - from: swagger-document
    where: $.definitions
    transform: |
      $["AudioTranscriptionOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["AudioTranslationOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["ChatCompletionsOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["CompletionsOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["EmbeddingsOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["ImageGenerationOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
```

## Polymorphic adjustments

The polymorphic _input_ models all expose the discriminator but it's ignored when serializing 
(ie, each type already knows the value and fills it in). So we'll just hide it.

`ChatRequestMessageClassification.Role`

```yaml
directive:
  - from: swagger-document
    where: $.definitions.ChatRequestMessage
    transform: $.properties.role["x-ms-client-name"] = "InternalRoleRename"
  - from:
    - models.go
    - models_serde.go
    where: $
    transform: return $.replace(/InternalRoleRename/g, "role")
```

`AzureChatExtensionConfigurationClassification.Type`

```yaml
directive:
  - from: swagger-document
    where: $.definitions.AzureChatExtensionConfiguration
    transform: $.properties.type["x-ms-client-name"] = "InternalChatExtensionTypeRename"
  - from:
    - models.go
    - models_serde.go
    where: $
    transform: return $.replace(/InternalChatExtensionTypeRename/g, "configType")
```

`OnYourDataAuthenticationOptionsClassification.Type`

```yaml
directive:
  - from: swagger-document
    where: $.definitions.OnYourDataAuthenticationOptions
    transform: $.properties.type["x-ms-client-name"] = "InternalOYDAuthTypeRename"
  - from:
    - models.go
    - models_serde.go
    where: $
    transform: return $.replace(/InternalOYDAuthTypeRename/g, "configType")
```

`ChatCompletionsResponseFormat.Type`

```yaml
directive:
  - from: swagger-document
    where: $.definitions.ChatCompletionsResponseFormat
    transform: $.properties.type["x-ms-client-name"] = "InternalChatCompletionsResponseFormat"
  - from:
    - models.go
    - models_serde.go
    where: $
    transform: return $.replace(/InternalChatCompletionsResponseFormat/g, "respType")
```

## Model -> DeploymentName

```yaml
directive:
  - from: swagger-document
    where: $.definitions
    transform: |
      $["AudioTranscriptionOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["AudioTranslationOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["ChatCompletionsOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["CompletionsOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["EmbeddingsOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
      $["ImageGenerationOptions"].properties["model"]["x-ms-client-name"] = "DeploymentName";
```

## Cleanup the audio transcription APIs

We're wrapping the audio translation and transcription APIs, so we can eliminate some of 
these autogenerated models and functions.

```yaml
directive:
  # kill models
  - from:
    - models.go
    - models_serde.go
    where: $
    transform: |
      return $
        .replace(/type XMSPathsHksgfdDeploymentsDeploymentidAudioTranscriptionsOverloadGetaudiotranscriptionasresponseobjectPostRequestbodyContentMultipartFormDataSchema struct.+?\n}/s, "")
        .replace(/\/\/ MarshalJSON implements the json.Marshaller interface for type XMSPathsHksgfdDeploymentsDeploymentidAudioTranscriptionsOverloadGetaudiotranscriptionasresponseobjectPostRequestbodyContentMultipartFormDataSchema.+?\n}/s, "")
        .replace(/\/\/ UnmarshalJSON implements the json.Unmarshaller interface for type XMSPathsHksgfdDeploymentsDeploymentidAudioTranscriptionsOverloadGetaudiotranscriptionasresponseobjectPostRequestbodyContentMultipartFormDataSchema.+?\n}/s, "")
        .replace(/type XMSPaths1Ak7Ov3DeploymentsDeploymentidAudioTranslationsOverloadGetaudiotranslationasresponseobjectPostRequestbodyContentMultipartFormDataSchema struct.+?\n}/s, "")
        .replace(/\/\/ MarshalJSON implements the json.Marshaller interface for type XMSPaths1Ak7Ov3DeploymentsDeploymentidAudioTranslationsOverloadGetaudiotranslationasresponseobjectPostRequestbodyContentMultipartFormDataSchema.+?\n}/s, "")
        .replace(/\/\/ UnmarshalJSON implements the json.Unmarshaller interface for type XMSPaths1Ak7Ov3DeploymentsDeploymentidAudioTranslationsOverloadGetaudiotranslationasresponseobjectPostRequestbodyContentMultipartFormDataSchema.+?\n}/s, "");
  # kill API functions
  - from:
    - client.go
    where: $
    transform: |
      return $
        .replace(/\/\/ GetAudioTranscriptionAsPlainText -.+?\n}/s, "")
        .replace(/\/\/ GetAudioTransclationAsPlainText -.+?\n}/s, "");
```

## Move the Azure extensions into their own section of the options

```yaml
# TODO: rename and move.
```


We've moved these 'extension' data types into their own field. 

```yaml
directive:
- from: swagger-document
  where: $.definitions.ChatCompletionsOptions.properties.dataSources
  transform: $["x-ms-client-name"] = "AzureExtensionsOptions"
```

## Trim the Error object to match our Go conventions
 
```yaml
directive:
  - from: swagger-document
    where: $.definitions["Azure.Core.Foundations.Error"]
    transform: |
      $.properties = { 
        code: $.properties["code"],
        message: {
          ...$.properties["message"],
          "x-ms-client-name": "InternalErrorMessageRename"
        },
      };
      $["x-ms-client-name"] = "Error";
  
  - from: swagger-document
    where: $.definitions
    transform: delete $["Azure.Core.Foundations.InnerError"];

  - from: 
    - models.go
    - models_serde.go
    where: $
    transform: return $.replace(/InternalErrorMessageRename/g, "message");
```

## Splice in some hooks for custom code

```yaml
directive:
  # Allow interception of formatting the URL path
  - from: client.go
    where: $
    transform: |
      return $
        .replace(/runtime\.JoinPaths\(client.endpoint, urlPath\)/g, "client.formatURL(urlPath, getDeployment(body))");

  # Allow custom parsing of the returned error, mostly for handling the content filtering errors.
  - from: client.go
    where: $
    transform: return $.replace(/runtime\.NewResponseError/sg, "client.newError");
```

Other misc fixes

```yaml
directive:
  - from: swagger-document
    where: $..paths["/deployments/{deploymentId}/completions"].post.requestBody
    transform: $["required"] = true;
  - from: swagger-document
    where: $.paths["/deployments/{deploymentId}/embeddings"].post.requestBody
    transform: $["required"] = true;

  # get rid of these auto-generated LRO status methods that aren't exposed.
  - from: swagger-document
    where: $.paths
    transform: delete $["/operations/images/{operationId}"]

  # Remove stream property from CompletionsOptions and ChatCompletionsOptions
  - from: swagger-document
    where: $.definitions["CompletionsOptions"]
    transform: delete $.properties.stream;
  - from: swagger-document
    where: $.definitions["ChatCompletionsOptions"]
    transform: delete $.properties.stream; 
```

Changes for audio/whisper APIs.

```yaml
directive:
  # the whisper operations are really long since they are a conglomeration of _all_ the
  # possible return types.
  - rename-operation:
      from: GetAudioTranscriptionAsResponseObject
      to: GetAudioTranscriptionInternal
  - rename-operation:
      from: GetAudioTranslationAsResponseObject
      to: GetAudioTranslationInternal

  - from: swagger-document
    where: $["x-ms-paths"]["/deployments/{deploymentId}/audio/translations?_overload=getAudioTranslationAsResponseObject"].post
    transform: $.operationId = "GetAudioTranslationInternal"
  - from: swagger-document
    where: $["x-ms-paths"]["/deployments/{deploymentId}/audio/transcriptions?_overload=getAudioTranscriptionAsResponseObject"].post
    transform: $.operationId = "GetAudioTranscriptionInternal"

  # hide the generated functions, in favor of our public wrappers.
  - from: 
    - client.go
    - models.go
    - models_serde.go
    - response_types.go
    - options.go
    where: $
    transform: |
      return $
        .replace(/GetAudioTranscriptionInternal([^){ ]*)/g, "getAudioTranscriptionInternal$1")
        .replace(/GetAudioTranslationInternal([^){ ]*)/g, "getAudioTranslationInternal$1");

  # some multipart fixing
  - from: client.go
    where: $
    transform: |
      return $
        .replace(/(func.*getAudio(?:Translation|Transcription)InternalCreateRequest\(.+?)options/g, "$1body")
        .replace(/runtime\.SetMultipartFormData\(.+?\)/sg, "setMultipartFormData(req, file, *body)")

  # response type parsing (can be text/plain _or_ JSON)
  - from: client.go
    where: $
    transform: |
      return $
      .replace(/client\.getAudioTranscriptionInternalHandleResponse/g, "getAudioTranscriptionInternalHandleResponse")
        .replace(/client\.getAudioTranslationInternalHandleResponse/g, "getAudioTranslationInternalHandleResponse")

  # fix the file parameter to be a []byte.
  - from: client.go
    where: $
    transform: return $.replace(/^(func \(client \*Client\) getAudioTrans.+?)file string,(.+)$/mg, "$1file []byte,$2")
```

## Logprob casing fixes

```yaml
directive:
  - from:
    - options.go
    - models_serde.go
    - models.go
    where: $
    transform: |
      return $
        .replace(/AvgLogprob \*float32/g, "AvgLogProb *float32")
        .replace(/(a|c)\.AvgLogprob/g, "$1.AvgLogProb")
  - from: 
    - client.go
    - models.go
    - models_serde.go
    - options.go
    - response_types.go
    where: $
    transform: return $.replace(/Logprobs/g, "LogProbs")
```

```yaml
directive:
  #
  # strip out the deploymentID validation code - we absorbed this into the endpoint.
  #
	# urlPath := "/deployments/{deploymentId}/embeddings"
	# if client.deploymentID == "" {
	# 	return nil, errors.New("parameter client.deploymentID cannot be empty")
	# }
	# urlPath = strings.ReplaceAll(urlPath, "{deploymentId}", url.PathEscape(client.deploymentID))
  - from: client.go
    where: $
    transform: >-
      return $.replace(
        /(\s+)urlPath\s*:=\s*"\/deployments\/\{deploymentId\}\/([^"]+)".+?url\.PathEscape.+?\n/gs, 
        "$1urlPath := \"$2\"\n")

  # splice out the auto-generated `deploymentID` field from the client
  - from: client.go
    where: $
    transform: >-
      return $.replace(
        /(type Client struct[^}]+})/s, 
        "type Client struct {\ninternal *azcore.Client; clientData;\n}")

  - from: 
    - models_serde.go
    - models.go
    where: $
    transform: | 
      return $
        // remove some types that were generated to support the recursive error.
        .replace(/\/\/ AzureCoreFoundationsInnerErrorInnererror.+?\n}/s, "")
        // also, remove its marshalling functions
        .replace(/\/\/ (Unmarshal|Marshal)JSON implements[^\n]+?for type AzureCoreFoundationsInnerErrorInnererror.+?\n}/sg, "")
        .replace(/\/\/ AzureCoreFoundationsErrorInnererror.+?\n}/s, "")
        .replace(/\/\/ (Unmarshal|Marshal)JSON implements[^\n]+?for type AzureCoreFoundationsErrorInnererror.+?\n}/sg, "")
        .replace(/\/\/ AzureCoreFoundationsErrorResponseError.+?\n}/s, "")
        .replace(/\/\/ (Unmarshal|Marshal)JSON implements[^\n]+?for type AzureCoreFoundationsErrorResponseError.+?\n}/sg, "")
        .replace(/\/\/ AzureCoreFoundationsErrorResponse.+?\n}/s, "")
        .replace(/\/\/ (Unmarshal|Marshal)JSON implements[^\n]+?for type AzureCoreFoundationsErrorResponse.+?\n}/sg, "")

        // Remove any references to the type and replace them with InnerError.
        .replace(/Innererror \*(AzureCoreFoundationsInnerErrorInnererror|AzureCoreFoundationsErrorInnererror)/g, "InnerError *InnerError")

        // Fix the marshallers/unmarshallers to use the right case.
        .replace(/(a|c).Innererror/g, '$1.InnerError')

        // We have two "inner error" types that are identical (ErrorInnerError and InnerError). Let's eliminate the one that's not actually directly referenced.
        .replace(/\/\/azureCoreFoundationsInnerError.+?\n}/s, "")
        
        //
        // Fix the AzureCoreFoundation naming to match our style.
        //
        .replace(/AzureCoreFoundations/g, "")
  - from: constants.go
    where: $
    transform: >-
      return $.replace(
        /type ServiceAPIVersions string.+PossibleServiceAPIVersionsValues.+?\n}/gs, 
        "")

  # delete client name prefix from method options and response types
  - from:
      - client.go
      - models.go
      - options.go
      - response_types.go
    where: $
    transform: return $.replace(/Client(\w+)((?:Options|Response))/g, "$1$2");

  # Make the Azure extensions internal - we expose these through the GetChatCompletions*() functions
  # and just treat which endpoint we use as an implementation detail.
  - from: client.go
    where: $
    transform: |
      return $
        .replace(/GetChatCompletionsWithAzureExtensions([ (])/g, "getChatCompletionsWithAzureExtensions$1")
        .replace(/GetChatCompletions([ (])/g, "getChatCompletions$1");
```

## Workarounds

This handles a case where (depending on mixture of older and newer resources) we can potentially see
_either_ of these fields that represents the same data (prompt filter results).

```yaml
directive:
  - from: models_serde.go
    where: $
    transform: return $.replace(/case "prompt_filter_results":/g, 'case "prompt_annotations":\nfallthrough\ncase "prompt_filter_results":')
```

Update the ChatRequestUserMessage to allow for []ChatCompletionRequestMessageContentPartText _or_
a string.

```yaml
directive:
  - from: models.go
    where: $
    transform: return $.replace(/Content any/g, 'Content ChatRequestUserMessageContent')
```

Add in some types that are incorrectly not being exported in the generation

```yaml
directive:
  - from: swagger-document
    where: $.definitions
    transform: |
      $["ChatCompletionRequestMessageContentPartType"] = {
        "type": "string",
        "enum": [
          "image_url",
          "text"
        ],
        "description": "The type of the content part.",
        "x-ms-enum": {
          "name": "ChatCompletionRequestMessageContentPartType",
          "modelAsString": true,
          "values": [
            {
              "name": "image_url",
              "value": "image_url",
              "description": "Chat content contains an image URL"
            },
            {
              "name": "text",
              "value": "text",
              "description": "Chat content contains text"
            },
          ]
        }
      };
      $["ChatCompletionRequestMessageContentPart"] = {
        "title": "represents either an image URL or text content for a prompt",
        "type": "object",
        "discriminator": "type",
        "properties": {
          "type": {
            "$ref": "#/definitions/ChatCompletionRequestMessageContentPartType"
          }
        },
        "required": [
          "type"
        ],
      };
      $["ChatCompletionRequestMessageContentPartImage"] = {
        "type": "object",
        "title": "represents an image URL, to be used as part of a prompt",
        "properties": {
          "image_url": {
            "type": "object",
            "title": "contains the URL and level of detail for an image prompt",
            "properties": {
              "url": {
                "type": "string",
                "description": "Either a URL of the image or the base64 encoded image data.",
                "format": "uri"
              },
              "detail": {
                "type": "string",
                "description": "Specifies the detail level of the image. Learn more in the [Vision guide](/docs/guides/vision/low-or-high-fidelity-image-understanding).",
                "enum": [
                  "auto",
                  "low",
                  "high"
                ],
                "default": "auto"
              }
            },
            "required": [
              "url"
            ]
          }
        },
        "allOf": [
          {
            "$ref": "#/definitions/ChatCompletionRequestMessageContentPart"
          }
        ],
        "required": [
          "image_url"
        ],
        "x-ms-discriminator-value": "image_url"
      };
      $["ChatCompletionRequestMessageContentPartText"] = {
        "type": "object",
        "title": "represents text content, to be used as part of a prompt",
        "properties": {
          "text": {
            "type": "string",
            "description": "The text content."
          }
        },
        "allOf": [
          {
            "$ref": "#/definitions/ChatCompletionRequestMessageContentPart"
          }
        ],
        "required": [
          "text"
        ],
        "x-ms-discriminator-value": "text"
      };
```

Polymorphic removal of the Type field: `ChatCompletionRequestMessageContentPartClassification.Type`

```yaml
directive:
  - from: swagger-document
    where: $.definitions.ChatCompletionRequestMessageContentPart
    transform: $.properties.type["x-ms-client-name"] = "ChatCompletionRequestMessageContentPartTypeRename"
  - from:
    - models.go
    - models_serde.go
    where: $
    transform: return $.replace(/ChatCompletionRequestMessageContentPartTypeRename/g, "partType")
```

Another workaround - streaming results don't contain the discriminator field so we'll
inject it when we can infer it properly ('function' property exists).

```yaml
directive:
  - from: polymorphic_helpers.go
    where: $
    transform: |
      return $.replace(/(func unmarshalChatCompletionsToolCallClassification.+?var b ChatCompletionsToolCallClassification\n)/s, 
        `$1\n` + 
        `if m["type"] == nil && m["function"] != nil {\n` +
        `  // WORKAROUND: the streaming results don't contain the proper role for functions, so we need to add these in.\n` +
        `  m["type"] = string(ChatRoleFunction)\n` + 
        `}\n`);
```

Add in the older style of function call as that's still the only way to talk to older models.

```yaml
directive:
  - from: models.go
    where: $
    transform: |
      const text = 
        `// Controls how the model responds to function calls. "none" means the model does not call a function, and responds to the\n` + 
        `// end-user. "auto" means the model can pick between an end-user or calling a\n` + 
        `// function. Specifying a particular function via {"name": "my_function"} forces the model to call that function. "none" is\n` + 
        `// the default when no functions are present. "auto" is the default if functions\n` + 
        `// are present.\n` + 
        `FunctionCall *ChatCompletionsOptionsFunctionCall\n` + 
        `\n` + 
        `// A list of functions the model may generate JSON inputs for.\n` + 
        `Functions []FunctionDefinition\n`;

      return $.replace(/(type ChatCompletionsOptions struct \{.+?FrequencyPenalty \*float32)/s, "$1\n\n" + text);
  - from: models_serde.go
    where: $
    transform: |
      const populateLines = 
        `populate(objectMap, "function_call", c.FunctionCall)\n` +
        `populate(objectMap, "functions", c.Functions)`;

      return $.replace(/(func \(c ChatCompletionsOptions\) MarshalJSON\(\).+?populate\(objectMap, "frequency_penalty", c.FrequencyPenalty\))/s, "$1\n" + populateLines)
```

Fix ToolChoice discriminated union

```yaml
directive:
  - from: swagger-document
    where: $.definitions.ChatCompletionsOptions.properties
    transform: $["tool_choice"]["x-ms-client-name"] = "ToolChoiceRenameMe"
  - from: 
    - models.go
    - models_serde.go
    where: $
    transform: |
      return $
        .replace(/^\s+ToolChoiceRenameMe.+$/m, "ToolChoice *ChatCompletionsToolChoice")   // update the name _and_ type for the field
        .replace(/ToolChoiceRenameMe/g, "ToolChoice")    // rename all other references
        .replace(/populateAny\(objectMap, "tool_choice", c\.ToolChoice\)/, 'populate(objectMap, "tool_choice", c.ToolChoice)');   // treat field as typed so nil means omit.
```
