Full Code of sashabaranov/go-openai for AI

master 5d7a276f4c0e cached
105 files
529.2 KB
144.9k tokens
970 symbols
1 requests
Download .txt
Showing preview only (558K chars total). Download the full file or copy to clipboard to get everything.
Repository: sashabaranov/go-openai
Branch: master
Commit: 5d7a276f4c0e
Files: 105
Total size: 529.2 KB

Directory structure:
gitextract_r1hjdmvj/

├── .codecov.yml
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── close-inactive-issues.yml
│       ├── integration-tests.yml
│       └── pr.yml
├── .gitignore
├── .golangci.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── api_integration_test.go
├── api_internal_test.go
├── assistant.go
├── assistant_test.go
├── audio.go
├── audio_api_test.go
├── audio_test.go
├── batch.go
├── batch_test.go
├── chat.go
├── chat_stream.go
├── chat_stream_test.go
├── chat_test.go
├── client.go
├── client_test.go
├── common.go
├── completion.go
├── completion_test.go
├── config.go
├── config_test.go
├── edits.go
├── edits_test.go
├── embeddings.go
├── embeddings_test.go
├── engines.go
├── engines_test.go
├── error.go
├── error_test.go
├── example_test.go
├── examples/
│   ├── README.md
│   ├── chatbot/
│   │   └── main.go
│   ├── completion/
│   │   └── main.go
│   ├── completion-with-tool/
│   │   └── main.go
│   ├── images/
│   │   └── main.go
│   └── voice-to-text/
│       └── main.go
├── files.go
├── files_api_test.go
├── files_test.go
├── fine_tunes.go
├── fine_tunes_test.go
├── fine_tuning_job.go
├── fine_tuning_job_test.go
├── go.mod
├── image.go
├── image_api_test.go
├── image_test.go
├── internal/
│   ├── error_accumulator.go
│   ├── error_accumulator_test.go
│   ├── form_builder.go
│   ├── form_builder_test.go
│   ├── marshaller.go
│   ├── marshaller_test.go
│   ├── request_builder.go
│   ├── request_builder_test.go
│   ├── test/
│   │   ├── checks/
│   │   │   ├── checks.go
│   │   │   └── checks_test.go
│   │   ├── failer.go
│   │   ├── failer_test.go
│   │   ├── helpers.go
│   │   ├── helpers_test.go
│   │   ├── server.go
│   │   └── server_test.go
│   ├── unmarshaler.go
│   └── unmarshaler_test.go
├── jsonschema/
│   ├── containsref_test.go
│   ├── json.go
│   ├── json_additional_test.go
│   ├── json_errors_test.go
│   ├── json_test.go
│   ├── validate.go
│   └── validate_test.go
├── messages.go
├── messages_test.go
├── models.go
├── models_test.go
├── moderation.go
├── moderation_test.go
├── openai_test.go
├── ratelimit.go
├── reasoning_validator.go
├── run.go
├── run_test.go
├── speech.go
├── speech_test.go
├── stream.go
├── stream_reader.go
├── stream_reader_test.go
├── stream_test.go
├── thread.go
├── thread_test.go
├── vector_store.go
└── vector_store_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .codecov.yml
================================================
coverage:
  ignore:
    - "examples/**"
    - "internal/test/**"


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [sashabaranov, vvatanabe]


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

Your issue may already be reported!
Please search on the [issue tracker](https://github.com/sashabaranov/go-openai/issues) before creating one.

**Describe the bug**
A clear and concise description of what the bug is. If it's an API-related bug, please provide relevant endpoint(s).

**To Reproduce**
Steps to reproduce the behavior, including any relevant code snippets.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots/Logs**
If applicable, add screenshots to help explain your problem. For non-graphical issues, please provide any relevant logs or stack traces.

**Environment (please complete the following information):**
 - go-openai version: [e.g. v1.12.0]
 - Go version: [e.g. 1.18]
 - OpenAI API version: [e.g. v1]
 - OS: [e.g. Ubuntu 20.04]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''

---

Your issue may already be reported!
Please search on the [issue tracker](https://github.com/sashabaranov/go-openai/issues) before creating one.

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
A similar PR may already be submitted!
Please search among the [Pull request](https://github.com/sashabaranov/go-openai/pulls) before creating one.

If your changes introduce breaking changes, please prefix the title of your pull request with "[BREAKING_CHANGES]". This allows for clear identification of such changes in the 'What's Changed' section on the release page, making it developer-friendly.

Thanks for submitting a pull request! Please provide enough information so that others can review your pull request.

**Describe the change**
Please provide a clear and concise description of the changes you're proposing. Explain what problem it solves or what feature it adds.

**Provide OpenAI documentation link**
Provide a relevant API doc from https://platform.openai.com/docs/api-reference

**Describe your solution**
Describe how your changes address the problem or how they add the feature. This should include a brief description of your approach and any new libraries or dependencies you're using.

**Tests**
Briefly describe how you have tested these changes. If possible — please add integration tests.

**Additional context**
Add any other context or screenshots or logs about your pull request here. If the pull request relates to an open issue, please link to it.

Issue: #XXXX


================================================
FILE: .github/workflows/close-inactive-issues.yml
================================================
name: Close inactive issues
on:
  schedule:
    - cron: "30 1 * * *"

jobs:
  close-issues:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - uses: actions/stale@v9
        with:
          days-before-issue-stale: 30
          days-before-issue-close: 14
          stale-issue-label: "stale"
          exempt-issue-labels: 'bug,enhancement'
          stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
          close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
          days-before-pr-stale: -1
          days-before-pr-close: -1
          repo-token: ${{ secrets.GITHUB_TOKEN }}

================================================
FILE: .github/workflows/integration-tests.yml
================================================
name: Integration tests

on:
  push:
    branches:
      - master

jobs:
  integration_tests:
    name: Run integration tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: '1.21'
    - name: Run integration tests
      env:
        OPENAI_TOKEN: ${{ secrets.OPENAI_TOKEN }}
      run: go test -v -tags=integration ./api_integration_test.go


================================================
FILE: .github/workflows/pr.yml
================================================
name: Sanity check

on:
  - push
  - pull_request

jobs:
  prcheck:
    name: Sanity check
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: '1.24'
    - name: Run vet
      run: |
        go vet -stdversion ./...
    - name: Run golangci-lint
      uses: golangci/golangci-lint-action@v7
      with:
        version: v2.1.5
    - name: Run tests
      run: go test -race -covermode=atomic -coverprofile=coverage.out -v ./...
    - name: Upload coverage reports to Codecov
      uses: codecov/codecov-action@v5
      with:
        token: ${{ secrets.CODECOV_TOKEN }}


================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Auth token for tests
.openai-token
.idea

# Generated by tests
test.mp3

================================================
FILE: .golangci.yml
================================================
version: "2"
linters:
  default: none
  enable:
    - asciicheck
    - bidichk
    - bodyclose
    - contextcheck
    - cyclop
    - dupl
    - durationcheck
    - errcheck
    - errname
    - errorlint
    - exhaustive
    - forbidigo
    - funlen
    - gochecknoinits
    - gocognit
    - goconst
    - gocritic
    - gocyclo
    - godot
    - gomoddirectives
    - gomodguard
    - goprintffuncname
    - gosec
    - govet
    - ineffassign
    - lll
    - makezero
    - mnd
    - nestif
    - nilerr
    - nilnil
    - nolintlint
    - nosprintfhostport
    - predeclared
    - promlinter
    - revive
    - rowserrcheck
    - sqlclosecheck
    - staticcheck
    - testpackage
    - tparallel
    - unconvert
    - unparam
    - unused
    - usetesting
    - wastedassign
    - whitespace
  settings:
    cyclop:
      max-complexity: 30
      package-average: 10
    errcheck:
      check-type-assertions: true
    funlen:
      lines: 100
      statements: 50
    gocognit:
      min-complexity: 20
    gocritic:
      settings:
        captLocal:
          paramsOnly: false
        underef:
          skipRecvDeref: false
    gomodguard:
      blocked:
        modules:
          - github.com/golang/protobuf:
              recommendations:
                - google.golang.org/protobuf
              reason: see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules
          - github.com/satori/go.uuid:
              recommendations:
                - github.com/google/uuid
              reason: satori's package is not maintained
          - github.com/gofrs/uuid:
              recommendations:
                - github.com/google/uuid
              reason: 'see recommendation from dev-infra team: https://confluence.gtforge.com/x/gQI6Aw'
    govet:
      disable:
        - fieldalignment
      enable-all: true
      settings:
        shadow:
          strict: true
    mnd:
      ignored-functions:
        - os.Chmod
        - os.Mkdir
        - os.MkdirAll
        - os.OpenFile
        - os.WriteFile
        - prometheus.ExponentialBuckets
        - prometheus.ExponentialBucketsRange
        - prometheus.LinearBuckets
        - strconv.FormatFloat
        - strconv.FormatInt
        - strconv.FormatUint
        - strconv.ParseFloat
        - strconv.ParseInt
        - strconv.ParseUint
    nakedret:
      max-func-lines: 0
    nolintlint:
      require-explanation: true
      require-specific: true
      allow-no-explanation:
        - funlen
        - gocognit
        - lll
    rowserrcheck:
      packages:
        - github.com/jmoiron/sqlx
  exclusions:
    generated: lax
    presets:
      - comments
      - common-false-positives
      - legacy
      - std-error-handling
    rules:
      - linters:
          - forbidigo
          - mnd
          - revive
        path : ^examples/.*\.go$
      - linters:
          - lll
        source: ^//\s*go:generate\s
      - linters:
          - godot
        source: (noinspection|TODO)
      - linters:
          - gocritic
        source: //noinspection
      - linters:
          - errorlint
        source: ^\s+if _, ok := err\.\([^.]+\.InternalError\); ok {
      - linters:
          - bodyclose
          - dupl
          - funlen
          - goconst
          - gosec
          - noctx
          - wrapcheck
          - staticcheck
        path: _test\.go
    paths:
      - third_party$
      - builtin$
      - examples$
issues:
  max-same-issues: 50
formatters:
  enable:
    - goimports
  exclusions:
    generated: lax
    paths:
      - third_party$
      - builtin$
      - examples$


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guidelines

## Overview
Thank you for your interest in contributing to the "Go OpenAI" project! By following this guideline, we hope to ensure that your contributions are made smoothly and efficiently. The Go OpenAI project is licensed under the [Apache 2.0 License](https://github.com/sashabaranov/go-openai/blob/master/LICENSE), and we welcome contributions through GitHub pull requests.

## Reporting Bugs
If you discover a bug, first check the [GitHub Issues page](https://github.com/sashabaranov/go-openai/issues) to see if the issue has already been reported. If you're reporting a new issue, please use the "Bug report" template and provide detailed information about the problem, including steps to reproduce it.

## Suggesting Features
If you want to suggest a new feature or improvement, first check the [GitHub Issues page](https://github.com/sashabaranov/go-openai/issues) to ensure a similar suggestion hasn't already been made. Use the "Feature request" template to provide a detailed description of your suggestion.

## Reporting Vulnerabilities
If you identify a security concern, please use the "Report a security vulnerability" template on the [GitHub Issues page](https://github.com/sashabaranov/go-openai/issues) to share the details. This report will only be viewable to repository maintainers. You will be credited if the advisory is published.

## Questions for Users
If you have questions, please utilize [StackOverflow](https://stackoverflow.com/) or the [GitHub Discussions page](https://github.com/sashabaranov/go-openai/discussions).

## Contributing Code
There might already be a similar pull requests submitted! Please search for [pull requests](https://github.com/sashabaranov/go-openai/pulls) before creating one.

### Requirements for Merging a Pull Request

The requirements to accept a pull request are as follows:

- Features not provided by the OpenAI API will not be accepted.
- The functionality of the feature must match that of the official OpenAI API.
- All pull requests should be written in Go according to common conventions, formatted with `goimports`, and free of warnings from tools like `golangci-lint`.
- Include tests and ensure all tests pass.
- Maintain test coverage without any reduction.
- All pull requests require approval from at least one Go OpenAI maintainer.

**Note:**  
The merging method for pull requests in this repository is squash merge.

### Creating a Pull Request
- Fork the repository.
- Create a new branch and commit your changes.
- Push that branch to GitHub.
- Start a new Pull Request on GitHub. (Please use the pull request template to provide detailed information.)

**Note:**  
If your changes introduce breaking changes, please prefix your pull request title with "[BREAKING_CHANGES]".

### Code Style
In this project, we adhere to the standard coding style of Go. Your code should maintain consistency with the rest of the codebase. To achieve this, please format your code using tools like `goimports` and resolve any syntax or style issues with `golangci-lint`.

**Run goimports:**
```
go install golang.org/x/tools/cmd/goimports@latest
```

```
goimports -w .
```

**Run golangci-lint:**
```
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
```

```
golangci-lint run --out-format=github-actions
```

### Unit Test
Please create or update tests relevant to your changes. Ensure all tests run successfully to verify that your modifications do not adversely affect other functionalities.

**Run test:**
```
go test -v ./...
```

### Integration Test
Integration tests are requested against the production version of the OpenAI API. These tests will verify that the library is properly coded against the actual behavior of the API, and will  fail upon any incompatible change in the API.

**Notes:**
These tests send real network traffic to the OpenAI API and may reach rate limits. Temporary network problems may also cause the test to fail.

**Run integration test:**
```
OPENAI_TOKEN=XXX go test -v -tags=integration ./api_integration_test.go
```

If the `OPENAI_TOKEN` environment variable is not available, integration tests will be skipped.

---

We wholeheartedly welcome your active participation. Let's build an amazing project together!


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
# Go OpenAI
[![Go Reference](https://pkg.go.dev/badge/github.com/sashabaranov/go-openai.svg)](https://pkg.go.dev/github.com/sashabaranov/go-openai)
[![Go Report Card](https://goreportcard.com/badge/github.com/sashabaranov/go-openai)](https://goreportcard.com/report/github.com/sashabaranov/go-openai)
[![codecov](https://codecov.io/gh/sashabaranov/go-openai/branch/master/graph/badge.svg?token=bCbIfHLIsW)](https://codecov.io/gh/sashabaranov/go-openai)

This library provides unofficial Go clients for [OpenAI API](https://platform.openai.com/). We support: 

* ChatGPT 4o, o1
* GPT-3, GPT-4
* DALL·E 2, DALL·E 3, GPT Image 1
* Whisper

## Installation

```
go get github.com/sashabaranov/go-openai
```
Currently, go-openai requires Go version 1.18 or greater.


## Usage

### ChatGPT example usage:

```go
package main

import (
	"context"
	"fmt"
	openai "github.com/sashabaranov/go-openai"
)

func main() {
	client := openai.NewClient("your token")
	resp, err := client.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model: openai.GPT3Dot5Turbo,
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "Hello!",
				},
			},
		},
	)

	if err != nil {
		fmt.Printf("ChatCompletion error: %v\n", err)
		return
	}

	fmt.Println(resp.Choices[0].Message.Content)
}

```

### Getting an OpenAI API Key:

1. Visit the OpenAI website at [https://platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys).
2. If you don't have an account, click on "Sign Up" to create one. If you do, click "Log In".
3. Once logged in, navigate to your API key management page.
4. Click on "Create new secret key".
5. Enter a name for your new key, then click "Create secret key".
6. Your new API key will be displayed. Use this key to interact with the OpenAI API.

**Note:** Your API key is sensitive information. Do not share it with anyone.

### Other examples:

<details>
<summary>ChatGPT streaming completion</summary>

```go
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	openai "github.com/sashabaranov/go-openai"
)

func main() {
	c := openai.NewClient("your token")
	ctx := context.Background()

	req := openai.ChatCompletionRequest{
		Model:     openai.GPT3Dot5Turbo,
		MaxTokens: 20,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Lorem ipsum",
			},
		},
		Stream: true,
	}
	stream, err := c.CreateChatCompletionStream(ctx, req)
	if err != nil {
		fmt.Printf("ChatCompletionStream error: %v\n", err)
		return
	}
	defer stream.Close()

	fmt.Printf("Stream response: ")
	for {
		response, err := stream.Recv()
		if errors.Is(err, io.EOF) {
			fmt.Println("\nStream finished")
			return
		}

		if err != nil {
			fmt.Printf("\nStream error: %v\n", err)
			return
		}

		fmt.Printf(response.Choices[0].Delta.Content)
	}
}
```
</details>

<details>
<summary>GPT-3 completion</summary>

```go
package main

import (
	"context"
	"fmt"
	openai "github.com/sashabaranov/go-openai"
)

func main() {
	c := openai.NewClient("your token")
	ctx := context.Background()

	req := openai.CompletionRequest{
		Model:     openai.GPT3Babbage002,
		MaxTokens: 5,
		Prompt:    "Lorem ipsum",
	}
	resp, err := c.CreateCompletion(ctx, req)
	if err != nil {
		fmt.Printf("Completion error: %v\n", err)
		return
	}
	fmt.Println(resp.Choices[0].Text)
}
```
</details>

<details>
<summary>GPT-3 streaming completion</summary>

```go
package main

import (
	"errors"
	"context"
	"fmt"
	"io"
	openai "github.com/sashabaranov/go-openai"
)

func main() {
	c := openai.NewClient("your token")
	ctx := context.Background()

	req := openai.CompletionRequest{
		Model:     openai.GPT3Babbage002,
		MaxTokens: 5,
		Prompt:    "Lorem ipsum",
		Stream:    true,
	}
	stream, err := c.CreateCompletionStream(ctx, req)
	if err != nil {
		fmt.Printf("CompletionStream error: %v\n", err)
		return
	}
	defer stream.Close()

	for {
		response, err := stream.Recv()
		if errors.Is(err, io.EOF) {
			fmt.Println("Stream finished")
			return
		}

		if err != nil {
			fmt.Printf("Stream error: %v\n", err)
			return
		}


		fmt.Printf("Stream response: %v\n", response)
	}
}
```
</details>

<details>
<summary>Audio Speech-To-Text</summary>

```go
package main

import (
	"context"
	"fmt"

	openai "github.com/sashabaranov/go-openai"
)

func main() {
	c := openai.NewClient("your token")
	ctx := context.Background()

	req := openai.AudioRequest{
		Model:    openai.Whisper1,
		FilePath: "recording.mp3",
	}
	resp, err := c.CreateTranscription(ctx, req)
	if err != nil {
		fmt.Printf("Transcription error: %v\n", err)
		return
	}
	fmt.Println(resp.Text)
}
```
</details>

<details>
<summary>Audio Captions</summary>

```go
package main

import (
	"context"
	"fmt"
	"os"

	openai "github.com/sashabaranov/go-openai"
)

func main() {
	c := openai.NewClient(os.Getenv("OPENAI_KEY"))

	req := openai.AudioRequest{
		Model:    openai.Whisper1,
		FilePath: os.Args[1],
		Format:   openai.AudioResponseFormatSRT,
	}
	resp, err := c.CreateTranscription(context.Background(), req)
	if err != nil {
		fmt.Printf("Transcription error: %v\n", err)
		return
	}
	f, err := os.Create(os.Args[1] + ".srt")
	if err != nil {
		fmt.Printf("Could not open file: %v\n", err)
		return
	}
	defer f.Close()
	if _, err := f.WriteString(resp.Text); err != nil {
		fmt.Printf("Error writing to file: %v\n", err)
		return
	}
}
```
</details>

<details>
<summary>DALL-E 2 image generation</summary>

```go
package main

import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	openai "github.com/sashabaranov/go-openai"
	"image/png"
	"os"
)

func main() {
	c := openai.NewClient("your token")
	ctx := context.Background()

	// Sample image by link
	reqUrl := openai.ImageRequest{
		Prompt:         "Parrot on a skateboard performs a trick, cartoon style, natural light, high detail",
		Size:           openai.CreateImageSize256x256,
		ResponseFormat: openai.CreateImageResponseFormatURL,
		N:              1,
	}

	respUrl, err := c.CreateImage(ctx, reqUrl)
	if err != nil {
		fmt.Printf("Image creation error: %v\n", err)
		return
	}
	fmt.Println(respUrl.Data[0].URL)

	// Example image as base64
	reqBase64 := openai.ImageRequest{
		Prompt:         "Portrait of a humanoid parrot in a classic costume, high detail, realistic light, unreal engine",
		Size:           openai.CreateImageSize256x256,
		ResponseFormat: openai.CreateImageResponseFormatB64JSON,
		N:              1,
	}

	respBase64, err := c.CreateImage(ctx, reqBase64)
	if err != nil {
		fmt.Printf("Image creation error: %v\n", err)
		return
	}

	imgBytes, err := base64.StdEncoding.DecodeString(respBase64.Data[0].B64JSON)
	if err != nil {
		fmt.Printf("Base64 decode error: %v\n", err)
		return
	}

	r := bytes.NewReader(imgBytes)
	imgData, err := png.Decode(r)
	if err != nil {
		fmt.Printf("PNG decode error: %v\n", err)
		return
	}

	file, err := os.Create("example.png")
	if err != nil {
		fmt.Printf("File creation error: %v\n", err)
		return
	}
	defer file.Close()

	if err := png.Encode(file, imgData); err != nil {
		fmt.Printf("PNG encode error: %v\n", err)
		return
	}

	fmt.Println("The image was saved as example.png")
}

```
</details>

<details>
<summary>GPT Image 1 image generation</summary>

```go
package main

import (
	"context"
	"encoding/base64"
	"fmt"
	"os"

	openai "github.com/sashabaranov/go-openai"
)

func main() {
	c := openai.NewClient("your token")
	ctx := context.Background()

	req := openai.ImageRequest{
		Prompt:            "Parrot on a skateboard performing a trick. Large bold text \"SKATE MASTER\" banner at the bottom of the image. Cartoon style, natural light, high detail, 1:1 aspect ratio.",
		Background:        openai.CreateImageBackgroundOpaque,
		Model:             openai.CreateImageModelGptImage1,
		Size:              openai.CreateImageSize1024x1024,
		N:                 1,
		Quality:           openai.CreateImageQualityLow,
		OutputCompression: 100,
		OutputFormat:      openai.CreateImageOutputFormatJPEG,
		// Moderation: 		 openai.CreateImageModerationLow,
		// User: 					 "",
	}

	resp, err := c.CreateImage(ctx, req)
	if err != nil {
		fmt.Printf("Image creation Image generation with GPT Image 1error: %v\n", err)
		return
	}

	fmt.Println("Image Base64:", resp.Data[0].B64JSON)

	// Decode the base64 data
	imgBytes, err := base64.StdEncoding.DecodeString(resp.Data[0].B64JSON)
	if err != nil {
		fmt.Printf("Base64 decode error: %v\n", err)
		return
	}

	// Write image to file
	outputPath := "generated_image.jpg"
	err = os.WriteFile(outputPath, imgBytes, 0644)
	if err != nil {
		fmt.Printf("Failed to write image file: %v\n", err)
		return
	}

	fmt.Printf("The image was saved as %s\n", outputPath)
}
```
</details>

<details>
<summary>Configuring proxy</summary>

```go
config := openai.DefaultConfig("token")
proxyUrl, err := url.Parse("http://localhost:{port}")
if err != nil {
	panic(err)
}
transport := &http.Transport{
	Proxy: http.ProxyURL(proxyUrl),
}
config.HTTPClient = &http.Client{
	Transport: transport,
}

c := openai.NewClientWithConfig(config)
```

See also: https://pkg.go.dev/github.com/sashabaranov/go-openai#ClientConfig
</details>

<details>
<summary>ChatGPT support context</summary>

```go
package main

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/sashabaranov/go-openai"
)

func main() {
	client := openai.NewClient("your token")
	messages := make([]openai.ChatCompletionMessage, 0)
	reader := bufio.NewReader(os.Stdin)
	fmt.Println("Conversation")
	fmt.Println("---------------------")

	for {
		fmt.Print("-> ")
		text, _ := reader.ReadString('\n')
		// convert CRLF to LF
		text = strings.Replace(text, "\n", "", -1)
		messages = append(messages, openai.ChatCompletionMessage{
			Role:    openai.ChatMessageRoleUser,
			Content: text,
		})

		resp, err := client.CreateChatCompletion(
			context.Background(),
			openai.ChatCompletionRequest{
				Model:    openai.GPT3Dot5Turbo,
				Messages: messages,
			},
		)

		if err != nil {
			fmt.Printf("ChatCompletion error: %v\n", err)
			continue
		}

		content := resp.Choices[0].Message.Content
		messages = append(messages, openai.ChatCompletionMessage{
			Role:    openai.ChatMessageRoleAssistant,
			Content: content,
		})
		fmt.Println(content)
	}
}
```
</details>

<details>
<summary>Azure OpenAI ChatGPT</summary>

```go
package main

import (
	"context"
	"fmt"

	openai "github.com/sashabaranov/go-openai"
)

func main() {
	config := openai.DefaultAzureConfig("your Azure OpenAI Key", "https://your Azure OpenAI Endpoint")
	// If you use a deployment name different from the model name, you can customize the AzureModelMapperFunc function
	// config.AzureModelMapperFunc = func(model string) string {
	// 	azureModelMapping := map[string]string{
	// 		"gpt-3.5-turbo": "your gpt-3.5-turbo deployment name",
	// 	}
	// 	return azureModelMapping[model]
	// }

	client := openai.NewClientWithConfig(config)
	resp, err := client.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model: openai.GPT3Dot5Turbo,
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "Hello Azure OpenAI!",
				},
			},
		},
	)
	if err != nil {
		fmt.Printf("ChatCompletion error: %v\n", err)
		return
	}

	fmt.Println(resp.Choices[0].Message.Content)
}

```
</details>

<details>
<summary>Embedding Semantic Similarity</summary>

```go
package main

import (
	"context"
	"log"
	openai "github.com/sashabaranov/go-openai"

)

func main() {
	client := openai.NewClient("your-token")

	// Create an EmbeddingRequest for the user query
	queryReq := openai.EmbeddingRequest{
		Input: []string{"How many chucks would a woodchuck chuck"},
		Model: openai.AdaEmbeddingV2,
	}

	// Create an embedding for the user query
	queryResponse, err := client.CreateEmbeddings(context.Background(), queryReq)
	if err != nil {
		log.Fatal("Error creating query embedding:", err)
	}

	// Create an EmbeddingRequest for the target text
	targetReq := openai.EmbeddingRequest{
		Input: []string{"How many chucks would a woodchuck chuck if the woodchuck could chuck wood"},
		Model: openai.AdaEmbeddingV2,
	}

	// Create an embedding for the target text
	targetResponse, err := client.CreateEmbeddings(context.Background(), targetReq)
	if err != nil {
		log.Fatal("Error creating target embedding:", err)
	}

	// Now that we have the embeddings for the user query and the target text, we
	// can calculate their similarity.
	queryEmbedding := queryResponse.Data[0]
	targetEmbedding := targetResponse.Data[0]

	similarity, err := queryEmbedding.DotProduct(&targetEmbedding)
	if err != nil {
		log.Fatal("Error calculating dot product:", err)
	}

	log.Printf("The similarity score between the query and the target is %f", similarity)
}

```
</details>

<details>
<summary>Azure OpenAI Embeddings</summary>

```go
package main

import (
	"context"
	"fmt"

	openai "github.com/sashabaranov/go-openai"
)

func main() {

	config := openai.DefaultAzureConfig("your Azure OpenAI Key", "https://your Azure OpenAI Endpoint")
	config.APIVersion = "2023-05-15" // optional update to latest API version

	//If you use a deployment name different from the model name, you can customize the AzureModelMapperFunc function
	//config.AzureModelMapperFunc = func(model string) string {
	//    azureModelMapping := map[string]string{
	//        "gpt-3.5-turbo":"your gpt-3.5-turbo deployment name",
	//    }
	//    return azureModelMapping[model]
	//}

	input := "Text to vectorize"

	client := openai.NewClientWithConfig(config)
	resp, err := client.CreateEmbeddings(
		context.Background(),
		openai.EmbeddingRequest{
			Input: []string{input},
			Model: openai.AdaEmbeddingV2,
		})

	if err != nil {
		fmt.Printf("CreateEmbeddings error: %v\n", err)
		return
	}

	vectors := resp.Data[0].Embedding // []float32 with 1536 dimensions

	fmt.Println(vectors[:10], "...", vectors[len(vectors)-10:])
}
```
</details>

<details>
<summary>JSON Schema for function calling</summary>

It is now possible for chat completion to choose to call a function for more information ([see developer docs here](https://platform.openai.com/docs/guides/gpt/function-calling)).

In order to describe the type of functions that can be called, a JSON schema must be provided. Many JSON schema libraries exist and are more advanced than what we can offer in this library, however we have included a simple `jsonschema` package for those who want to use this feature without formatting their own JSON schema payload.

The developer documents give this JSON schema definition as an example:

```json
{
  "name":"get_current_weather",
  "description":"Get the current weather in a given location",
  "parameters":{
    "type":"object",
    "properties":{
        "location":{
          "type":"string",
          "description":"The city and state, e.g. San Francisco, CA"
        },
        "unit":{
          "type":"string",
          "enum":[
              "celsius",
              "fahrenheit"
          ]
        }
    },
    "required":[
        "location"
    ]
  }
}
```

Using the `jsonschema` package, this schema could be created using structs as such:

```go
FunctionDefinition{
  Name: "get_current_weather",
  Parameters: jsonschema.Definition{
    Type: jsonschema.Object,
    Properties: map[string]jsonschema.Definition{
      "location": {
        Type: jsonschema.String,
        Description: "The city and state, e.g. San Francisco, CA",
      },
      "unit": {
        Type: jsonschema.String,
        Enum: []string{"celsius", "fahrenheit"},
      },
    },
    Required: []string{"location"},
  },
}
```

The `Parameters` field of a `FunctionDefinition` can accept either of the above styles, or even a nested struct from another library (as long as it can be marshalled into JSON).
</details>

<details>
<summary>Error handling</summary>

Open-AI maintains clear documentation on how to [handle API errors](https://platform.openai.com/docs/guides/error-codes/api-errors)

example:
```
e := &openai.APIError{}
if errors.As(err, &e) {
  switch e.HTTPStatusCode {
    case 401:
      // invalid auth or key (do not retry)
    case 429:
      // rate limiting or engine overload (wait and retry) 
    case 500:
      // openai server error (retry)
    default:
      // unhandled
  }
}

```
</details>

<details>
<summary>Fine Tune Model</summary>

```go
package main

import (
	"context"
	"fmt"
	"github.com/sashabaranov/go-openai"
)

func main() {
	client := openai.NewClient("your token")
	ctx := context.Background()

	// create a .jsonl file with your training data for conversational model
	// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
	// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
	// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}

	// chat models are trained using the following file format:
	// {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
	// {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
	// {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}

	// you can use openai cli tool to validate the data
	// For more info - https://platform.openai.com/docs/guides/fine-tuning

	file, err := client.CreateFile(ctx, openai.FileRequest{
		FilePath: "training_prepared.jsonl",
		Purpose:  "fine-tune",
	})
	if err != nil {
		fmt.Printf("Upload JSONL file error: %v\n", err)
		return
	}

	// create a fine tuning job
	// Streams events until the job is done (this often takes minutes, but can take hours if there are many jobs in the queue or your dataset is large)
	// use below get method to know the status of your model
	fineTuningJob, err := client.CreateFineTuningJob(ctx, openai.FineTuningJobRequest{
		TrainingFile: file.ID,
		Model:        "davinci-002", // gpt-3.5-turbo-0613, babbage-002.
	})
	if err != nil {
		fmt.Printf("Creating new fine tune model error: %v\n", err)
		return
	}

	fineTuningJob, err = client.RetrieveFineTuningJob(ctx, fineTuningJob.ID)
	if err != nil {
		fmt.Printf("Getting fine tune model error: %v\n", err)
		return
	}
	fmt.Println(fineTuningJob.FineTunedModel)

	// once the status of fineTuningJob is `succeeded`, you can use your fine tune model in Completion Request or Chat Completion Request

	// resp, err := client.CreateCompletion(ctx, openai.CompletionRequest{
	//	 Model:  fineTuningJob.FineTunedModel,
	//	 Prompt: "your prompt",
	// })
	// if err != nil {
	//	 fmt.Printf("Create completion error %v\n", err)
	//	 return
	// }
	//
	// fmt.Println(resp.Choices[0].Text)
}
```
</details>

<details>
<summary>Structured Outputs</summary>

```go
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/jsonschema"
)

func main() {
	client := openai.NewClient("your token")
	ctx := context.Background()

	type Result struct {
		Steps []struct {
			Explanation string `json:"explanation"`
			Output      string `json:"output"`
		} `json:"steps"`
		FinalAnswer string `json:"final_answer"`
	}
	var result Result
	schema, err := jsonschema.GenerateSchemaForType(result)
	if err != nil {
		log.Fatalf("GenerateSchemaForType error: %v", err)
	}
	resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
		Model: openai.GPT4oMini,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleSystem,
				Content: "You are a helpful math tutor. Guide the user through the solution step by step.",
			},
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "how can I solve 8x + 7 = -23",
			},
		},
		ResponseFormat: &openai.ChatCompletionResponseFormat{
			Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
			JSONSchema: &openai.ChatCompletionResponseFormatJSONSchema{
				Name:   "math_reasoning",
				Schema: schema,
				Strict: true,
			},
		},
	})
	if err != nil {
		log.Fatalf("CreateChatCompletion error: %v", err)
	}
	err = schema.Unmarshal(resp.Choices[0].Message.Content, &result)
	if err != nil {
		log.Fatalf("Unmarshal schema error: %v", err)
	}
	fmt.Println(result)
}
```
</details>
See the `examples/` folder for more.

## Frequently Asked Questions

### Why don't we get the same answer when specifying a temperature field of 0 and asking the same question?

Even when specifying a temperature field of 0, it doesn't guarantee that you'll always get the same response. Several factors come into play.

1. Go OpenAI Behavior: When you specify a temperature field of 0 in Go OpenAI, the omitempty tag causes that field to be removed from the request. Consequently, the OpenAI API applies the default value of 1.
2. Token Count for Input/Output: If there's a large number of tokens in the input and output, setting the temperature to 0 can still result in non-deterministic behavior. In particular, when using around 32k tokens, the likelihood of non-deterministic behavior becomes highest even with a temperature of 0.

Due to the factors mentioned above, different answers may be returned even for the same question.

**Workarounds:**
1. As of November 2023, use [the new `seed` parameter](https://platform.openai.com/docs/guides/text-generation/reproducible-outputs) in conjunction with the `system_fingerprint` response field, alongside Temperature management.
2. Try using `math.SmallestNonzeroFloat32`: By specifying `math.SmallestNonzeroFloat32` in the temperature field instead of 0, you can mimic the behavior of setting it to 0.
3. Limiting Token Count: By limiting the number of tokens in the input and output and especially avoiding large requests close to 32k tokens, you can reduce the risk of non-deterministic behavior.

By adopting these strategies, you can expect more consistent results.

**Related Issues:**  
[omitempty option of request struct will generate incorrect request when parameter is 0.](https://github.com/sashabaranov/go-openai/issues/9)

### Does Go OpenAI provide a method to count tokens?

No, Go OpenAI does not offer a feature to count tokens, and there are no plans to provide such a feature in the future. However, if there's a way to implement a token counting feature with zero dependencies, it might be possible to merge that feature into Go OpenAI. Otherwise, it would be more appropriate to implement it in a dedicated library or repository.

For counting tokens, you might find the following links helpful:  
- [Counting Tokens For Chat API Calls](https://github.com/pkoukk/tiktoken-go#counting-tokens-for-chat-api-calls)
- [How to count tokens with tiktoken](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb)

**Related Issues:**  
[Is it possible to join the implementation of GPT3 Tokenizer](https://github.com/sashabaranov/go-openai/issues/62)

## Contributing

By following [Contributing Guidelines](https://github.com/sashabaranov/go-openai/blob/master/CONTRIBUTING.md), we hope to ensure that your contributions are made smoothly and efficiently.

## Thank you

We want to take a moment to express our deepest gratitude to the [contributors](https://github.com/sashabaranov/go-openai/graphs/contributors) and sponsors of this project:
- [Carson Kahn](https://carsonkahn.com) of [Spindle AI](https://spindleai.com)

To all of you: thank you. You've helped us achieve more than we ever imagined possible. Can't wait to see where we go next, together!


================================================
FILE: api_integration_test.go
================================================
//go:build integration

package openai_test

import (
	"context"
	"encoding/json"
	"errors"
	"io"
	"os"
	"testing"

	"github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/internal/test/checks"
	"github.com/sashabaranov/go-openai/jsonschema"
)

func TestAPI(t *testing.T) {
	apiToken := os.Getenv("OPENAI_TOKEN")
	if apiToken == "" {
		t.Skip("Skipping testing against production OpenAI API. Set OPENAI_TOKEN environment variable to enable it.")
	}

	var err error
	c := openai.NewClient(apiToken)
	ctx := context.Background()
	_, err = c.ListEngines(ctx)
	checks.NoError(t, err, "ListEngines error")

	_, err = c.GetEngine(ctx, openai.GPT3Davinci002)
	checks.NoError(t, err, "GetEngine error")

	fileRes, err := c.ListFiles(ctx)
	checks.NoError(t, err, "ListFiles error")

	if len(fileRes.Files) > 0 {
		_, err = c.GetFile(ctx, fileRes.Files[0].ID)
		checks.NoError(t, err, "GetFile error")
	} // else skip

	embeddingReq := openai.EmbeddingRequest{
		Input: []string{
			"The food was delicious and the waiter",
			"Other examples of embedding request",
		},
		Model: openai.AdaEmbeddingV2,
	}
	_, err = c.CreateEmbeddings(ctx, embeddingReq)
	checks.NoError(t, err, "Embedding error")

	_, err = c.CreateChatCompletion(
		ctx,
		openai.ChatCompletionRequest{
			Model: openai.GPT3Dot5Turbo,
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "Hello!",
				},
			},
		},
	)

	checks.NoError(t, err, "CreateChatCompletion (without name) returned error")

	_, err = c.CreateChatCompletion(
		ctx,
		openai.ChatCompletionRequest{
			Model: openai.GPT3Dot5Turbo,
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Name:    "John_Doe",
					Content: "Hello!",
				},
			},
		},
	)
	checks.NoError(t, err, "CreateChatCompletion (with name) returned error")

	_, err = c.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model: openai.GPT3Dot5Turbo,
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "What is the weather like in Boston?",
				},
			},
			Functions: []openai.FunctionDefinition{{
				Name: "get_current_weather",
				Parameters: jsonschema.Definition{
					Type: jsonschema.Object,
					Properties: map[string]jsonschema.Definition{
						"location": {
							Type:        jsonschema.String,
							Description: "The city and state, e.g. San Francisco, CA",
						},
						"unit": {
							Type: jsonschema.String,
							Enum: []string{"celsius", "fahrenheit"},
						},
					},
					Required: []string{"location"},
				},
			}},
		},
	)
	checks.NoError(t, err, "CreateChatCompletion (with functions) returned error")
}

func TestCompletionStream(t *testing.T) {
	apiToken := os.Getenv("OPENAI_TOKEN")
	if apiToken == "" {
		t.Skip("Skipping testing against production OpenAI API. Set OPENAI_TOKEN environment variable to enable it.")
	}

	c := openai.NewClient(apiToken)
	ctx := context.Background()

	stream, err := c.CreateCompletionStream(ctx, openai.CompletionRequest{
		Prompt:    "Ex falso quodlibet",
		Model:     openai.GPT3Babbage002,
		MaxTokens: 5,
		Stream:    true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	counter := 0
	for {
		_, err = stream.Recv()
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}
			t.Errorf("Stream error: %v", err)
		} else {
			counter++
		}
	}
	if counter == 0 {
		t.Error("Stream did not return any responses")
	}
}

func TestAPIError(t *testing.T) {
	apiToken := os.Getenv("OPENAI_TOKEN")
	if apiToken == "" {
		t.Skip("Skipping testing against production OpenAI API. Set OPENAI_TOKEN environment variable to enable it.")
	}

	var err error
	c := openai.NewClient(apiToken + "_invalid")
	ctx := context.Background()
	_, err = c.ListEngines(ctx)
	checks.HasError(t, err, "ListEngines should fail with an invalid key")

	var apiErr *openai.APIError
	if !errors.As(err, &apiErr) {
		t.Fatalf("Error is not an APIError: %+v", err)
	}

	if apiErr.HTTPStatusCode != 401 {
		t.Fatalf("Unexpected API error status code: %d", apiErr.HTTPStatusCode)
	}

	switch v := apiErr.Code.(type) {
	case string:
		if v != "invalid_api_key" {
			t.Fatalf("Unexpected API error code: %s", v)
		}
	default:
		t.Fatalf("Unexpected API error code type: %T", v)
	}

	if apiErr.Error() == "" {
		t.Fatal("Empty error message occurred")
	}
}

func TestChatCompletionResponseFormat_JSONSchema(t *testing.T) {
	apiToken := os.Getenv("OPENAI_TOKEN")
	if apiToken == "" {
		t.Skip("Skipping testing against production OpenAI API. Set OPENAI_TOKEN environment variable to enable it.")
	}

	var err error
	c := openai.NewClient(apiToken)
	ctx := context.Background()

	type MyStructuredResponse struct {
		PascalCase string `json:"pascal_case" required:"true" description:"PascalCase"`
		CamelCase  string `json:"camel_case" required:"true" description:"CamelCase"`
		KebabCase  string `json:"kebab_case" required:"true" description:"KebabCase"`
		SnakeCase  string `json:"snake_case" required:"true" description:"SnakeCase"`
	}
	var result MyStructuredResponse
	schema, err := jsonschema.GenerateSchemaForType(result)
	if err != nil {
		t.Fatal("CreateChatCompletion (use json_schema response) GenerateSchemaForType error")
	}
	resp, err := c.CreateChatCompletion(
		ctx,
		openai.ChatCompletionRequest{
			Model: openai.GPT4oMini,
			Messages: []openai.ChatCompletionMessage{
				{
					Role: openai.ChatMessageRoleSystem,
					Content: "Please enter a string, and we will convert it into the following naming conventions:" +
						"1. PascalCase: Each word starts with an uppercase letter, with no spaces or separators." +
						"2. CamelCase: The first word starts with a lowercase letter, " +
						"and subsequent words start with an uppercase letter, with no spaces or separators." +
						"3. KebabCase: All letters are lowercase, with words separated by hyphens `-`." +
						"4. SnakeCase: All letters are lowercase, with words separated by underscores `_`.",
				},
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "Hello World",
				},
			},
			ResponseFormat: &openai.ChatCompletionResponseFormat{
				Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
				JSONSchema: &openai.ChatCompletionResponseFormatJSONSchema{
					Name:   "cases",
					Schema: schema,
					Strict: true,
				},
			},
		},
	)
	checks.NoError(t, err, "CreateChatCompletion (use json_schema response) returned error")
	if err == nil {
		err = schema.Unmarshal(resp.Choices[0].Message.Content, &result)
		checks.NoError(t, err, "CreateChatCompletion (use json_schema response) unmarshal error")
	}
}

func TestChatCompletionStructuredOutputsFunctionCalling(t *testing.T) {
	apiToken := os.Getenv("OPENAI_TOKEN")
	if apiToken == "" {
		t.Skip("Skipping testing against production OpenAI API. Set OPENAI_TOKEN environment variable to enable it.")
	}

	var err error
	c := openai.NewClient(apiToken)
	ctx := context.Background()

	resp, err := c.CreateChatCompletion(
		ctx,
		openai.ChatCompletionRequest{
			Model: openai.GPT4oMini,
			Messages: []openai.ChatCompletionMessage{
				{
					Role: openai.ChatMessageRoleSystem,
					Content: "Please enter a string, and we will convert it into the following naming conventions:" +
						"1. PascalCase: Each word starts with an uppercase letter, with no spaces or separators." +
						"2. CamelCase: The first word starts with a lowercase letter, " +
						"and subsequent words start with an uppercase letter, with no spaces or separators." +
						"3. KebabCase: All letters are lowercase, with words separated by hyphens `-`." +
						"4. SnakeCase: All letters are lowercase, with words separated by underscores `_`.",
				},
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "Hello World",
				},
			},
			Tools: []openai.Tool{
				{
					Type: openai.ToolTypeFunction,
					Function: &openai.FunctionDefinition{
						Name:   "display_cases",
						Strict: true,
						Parameters: &jsonschema.Definition{
							Type: jsonschema.Object,
							Properties: map[string]jsonschema.Definition{
								"PascalCase": {
									Type: jsonschema.String,
								},
								"CamelCase": {
									Type: jsonschema.String,
								},
								"KebabCase": {
									Type: jsonschema.String,
								},
								"SnakeCase": {
									Type: jsonschema.String,
								},
							},
							Required:             []string{"PascalCase", "CamelCase", "KebabCase", "SnakeCase"},
							AdditionalProperties: false,
						},
					},
				},
			},
			ToolChoice: openai.ToolChoice{
				Type: openai.ToolTypeFunction,
				Function: openai.ToolFunction{
					Name: "display_cases",
				},
			},
		},
	)
	checks.NoError(t, err, "CreateChatCompletion (use structured outputs response) returned error")
	var result = make(map[string]string)
	err = json.Unmarshal([]byte(resp.Choices[0].Message.ToolCalls[0].Function.Arguments), &result)
	checks.NoError(t, err, "CreateChatCompletion (use structured outputs response) unmarshal error")
	for _, key := range []string{"PascalCase", "CamelCase", "KebabCase", "SnakeCase"} {
		if _, ok := result[key]; !ok {
			t.Errorf("key:%s does not exist.", key)
		}
	}
}


================================================
FILE: api_internal_test.go
================================================
package openai

import (
	"context"
	"testing"
)

func TestOpenAIFullURL(t *testing.T) {
	cases := []struct {
		Name   string
		Suffix string
		Expect string
	}{
		{
			"ChatCompletionsURL",
			"/chat/completions",
			"https://api.openai.com/v1/chat/completions",
		},
		{
			"CompletionsURL",
			"/completions",
			"https://api.openai.com/v1/completions",
		},
	}

	for _, c := range cases {
		t.Run(c.Name, func(t *testing.T) {
			az := DefaultConfig("dummy")
			cli := NewClientWithConfig(az)
			actual := cli.fullURL(c.Suffix)
			if actual != c.Expect {
				t.Errorf("Expected %s, got %s", c.Expect, actual)
			}
			t.Logf("Full URL: %s", actual)
		})
	}
}

func TestRequestAuthHeader(t *testing.T) {
	cases := []struct {
		Name      string
		APIType   APIType
		HeaderKey string
		Token     string
		OrgID     string
		Expect    string
	}{
		{
			"OpenAIDefault",
			"",
			"Authorization",
			"dummy-token-openai",
			"",
			"Bearer dummy-token-openai",
		},
		{
			"OpenAIOrg",
			APITypeOpenAI,
			"Authorization",
			"dummy-token-openai",
			"dummy-org-openai",
			"Bearer dummy-token-openai",
		},
		{
			"OpenAI",
			APITypeOpenAI,
			"Authorization",
			"dummy-token-openai",
			"",
			"Bearer dummy-token-openai",
		},
		{
			"AzureAD",
			APITypeAzureAD,
			"Authorization",
			"dummy-token-azure",
			"",
			"Bearer dummy-token-azure",
		},
		{
			"Azure",
			APITypeAzure,
			AzureAPIKeyHeader,
			"dummy-api-key-here",
			"",
			"dummy-api-key-here",
		},
	}

	for _, c := range cases {
		t.Run(c.Name, func(t *testing.T) {
			az := DefaultConfig(c.Token)
			az.APIType = c.APIType
			az.OrgID = c.OrgID

			cli := NewClientWithConfig(az)
			req, err := cli.newRequest(context.Background(), "POST", "/chat/completions")
			if err != nil {
				t.Errorf("Failed to create request: %v", err)
			}
			actual := req.Header.Get(c.HeaderKey)
			if actual != c.Expect {
				t.Errorf("Expected %s, got %s", c.Expect, actual)
			}
			t.Logf("%s: %s", c.HeaderKey, actual)
		})
	}
}

func TestAzureFullURL(t *testing.T) {
	cases := []struct {
		Name             string
		BaseURL          string
		AzureModelMapper map[string]string
		Suffix           string
		Model            string
		Expect           string
	}{
		{
			"AzureBaseURLWithSlashAutoStrip",
			"https://httpbin.org/",
			nil,
			"/chat/completions",
			"chatgpt-demo",
			"https://httpbin.org/" +
				"openai/deployments/chatgpt-demo" +
				"/chat/completions?api-version=2023-05-15",
		},
		{
			"AzureBaseURLWithoutSlashOK",
			"https://httpbin.org",
			nil,
			"/chat/completions",
			"chatgpt-demo",
			"https://httpbin.org/" +
				"openai/deployments/chatgpt-demo" +
				"/chat/completions?api-version=2023-05-15",
		},
		{
			"",
			"https://httpbin.org",
			nil,
			"/assistants?limit=10",
			"chatgpt-demo",
			"https://httpbin.org/openai/assistants?api-version=2023-05-15&limit=10",
		},
	}

	for _, c := range cases {
		t.Run(c.Name, func(t *testing.T) {
			az := DefaultAzureConfig("dummy", c.BaseURL)
			cli := NewClientWithConfig(az)
			// /openai/deployments/{engine}/chat/completions?api-version={api_version}
			actual := cli.fullURL(c.Suffix, withModel(c.Model))
			if actual != c.Expect {
				t.Errorf("Expected %s, got %s", c.Expect, actual)
			}
			t.Logf("Full URL: %s", actual)
		})
	}
}

func TestCloudflareAzureFullURL(t *testing.T) {
	cases := []struct {
		Name    string
		BaseURL string
		Suffix  string
		Expect  string
	}{
		{
			"CloudflareAzureBaseURLWithSlashAutoStrip",
			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo/",
			"/chat/completions",
			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo/" +
				"chat/completions?api-version=2023-05-15",
		},
		{
			"",
			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo",
			"/assistants?limit=10",
			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo" +
				"/assistants?api-version=2023-05-15&limit=10",
		},
	}

	for _, c := range cases {
		t.Run(c.Name, func(t *testing.T) {
			az := DefaultAzureConfig("dummy", c.BaseURL)
			az.APIType = APITypeCloudflareAzure

			cli := NewClientWithConfig(az)

			actual := cli.fullURL(c.Suffix)
			if actual != c.Expect {
				t.Errorf("Expected %s, got %s", c.Expect, actual)
			}
			t.Logf("Full URL: %s", actual)
		})
	}
}


================================================
FILE: assistant.go
================================================
package openai

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

const (
	assistantsSuffix      = "/assistants"
	assistantsFilesSuffix = "/files"
)

type Assistant struct {
	ID             string                 `json:"id"`
	Object         string                 `json:"object"`
	CreatedAt      int64                  `json:"created_at"`
	Name           *string                `json:"name,omitempty"`
	Description    *string                `json:"description,omitempty"`
	Model          string                 `json:"model"`
	Instructions   *string                `json:"instructions,omitempty"`
	Tools          []AssistantTool        `json:"tools"`
	ToolResources  *AssistantToolResource `json:"tool_resources,omitempty"`
	FileIDs        []string               `json:"file_ids,omitempty"` // Deprecated in v2
	Metadata       map[string]any         `json:"metadata,omitempty"`
	Temperature    *float32               `json:"temperature,omitempty"`
	TopP           *float32               `json:"top_p,omitempty"`
	ResponseFormat any                    `json:"response_format,omitempty"`

	httpHeader
}

type AssistantToolType string

const (
	AssistantToolTypeCodeInterpreter AssistantToolType = "code_interpreter"
	AssistantToolTypeRetrieval       AssistantToolType = "retrieval"
	AssistantToolTypeFunction        AssistantToolType = "function"
	AssistantToolTypeFileSearch      AssistantToolType = "file_search"
)

type AssistantTool struct {
	Type     AssistantToolType   `json:"type"`
	Function *FunctionDefinition `json:"function,omitempty"`
}

type AssistantToolFileSearch struct {
	VectorStoreIDs []string `json:"vector_store_ids"`
}

type AssistantToolCodeInterpreter struct {
	FileIDs []string `json:"file_ids"`
}

type AssistantToolResource struct {
	FileSearch      *AssistantToolFileSearch      `json:"file_search,omitempty"`
	CodeInterpreter *AssistantToolCodeInterpreter `json:"code_interpreter,omitempty"`
}

// AssistantRequest provides the assistant request parameters.
// When modifying the tools the API functions as the following:
// If Tools is undefined, no changes are made to the Assistant's tools.
// If Tools is empty slice it will effectively delete all of the Assistant's tools.
// If Tools is populated, it will replace all of the existing Assistant's tools with the provided tools.
type AssistantRequest struct {
	Model          string                 `json:"model"`
	Name           *string                `json:"name,omitempty"`
	Description    *string                `json:"description,omitempty"`
	Instructions   *string                `json:"instructions,omitempty"`
	Tools          []AssistantTool        `json:"-"`
	FileIDs        []string               `json:"file_ids,omitempty"`
	Metadata       map[string]any         `json:"metadata,omitempty"`
	ToolResources  *AssistantToolResource `json:"tool_resources,omitempty"`
	ResponseFormat any                    `json:"response_format,omitempty"`
	Temperature    *float32               `json:"temperature,omitempty"`
	TopP           *float32               `json:"top_p,omitempty"`
}

// MarshalJSON provides a custom marshaller for the assistant request to handle the API use cases
// If Tools is nil, the field is omitted from the JSON.
// If Tools is an empty slice, it's included in the JSON as an empty array ([]).
// If Tools is populated, it's included in the JSON with the elements.
func (a AssistantRequest) MarshalJSON() ([]byte, error) {
	type Alias AssistantRequest
	assistantAlias := &struct {
		Tools *[]AssistantTool `json:"tools,omitempty"`
		*Alias
	}{
		Alias: (*Alias)(&a),
	}

	if a.Tools != nil {
		assistantAlias.Tools = &a.Tools
	}

	return json.Marshal(assistantAlias)
}

// AssistantsList is a list of assistants.
type AssistantsList struct {
	Assistants []Assistant `json:"data"`
	LastID     *string     `json:"last_id"`
	FirstID    *string     `json:"first_id"`
	HasMore    bool        `json:"has_more"`
	httpHeader
}

type AssistantDeleteResponse struct {
	ID      string `json:"id"`
	Object  string `json:"object"`
	Deleted bool   `json:"deleted"`

	httpHeader
}

type AssistantFile struct {
	ID          string `json:"id"`
	Object      string `json:"object"`
	CreatedAt   int64  `json:"created_at"`
	AssistantID string `json:"assistant_id"`

	httpHeader
}

type AssistantFileRequest struct {
	FileID string `json:"file_id"`
}

type AssistantFilesList struct {
	AssistantFiles []AssistantFile `json:"data"`

	httpHeader
}

// CreateAssistant creates a new assistant.
func (c *Client) CreateAssistant(ctx context.Context, request AssistantRequest) (response Assistant, err error) {
	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(assistantsSuffix), withBody(request),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

// RetrieveAssistant retrieves an assistant.
func (c *Client) RetrieveAssistant(
	ctx context.Context,
	assistantID string,
) (response Assistant, err error) {
	urlSuffix := fmt.Sprintf("%s/%s", assistantsSuffix, assistantID)
	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

// ModifyAssistant modifies an assistant.
func (c *Client) ModifyAssistant(
	ctx context.Context,
	assistantID string,
	request AssistantRequest,
) (response Assistant, err error) {
	urlSuffix := fmt.Sprintf("%s/%s", assistantsSuffix, assistantID)
	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

// DeleteAssistant deletes an assistant.
func (c *Client) DeleteAssistant(
	ctx context.Context,
	assistantID string,
) (response AssistantDeleteResponse, err error) {
	urlSuffix := fmt.Sprintf("%s/%s", assistantsSuffix, assistantID)
	req, err := c.newRequest(ctx, http.MethodDelete, c.fullURL(urlSuffix),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

// ListAssistants Lists the currently available assistants.
func (c *Client) ListAssistants(
	ctx context.Context,
	limit *int,
	order *string,
	after *string,
	before *string,
) (response AssistantsList, err error) {
	urlValues := url.Values{}
	if limit != nil {
		urlValues.Add("limit", fmt.Sprintf("%d", *limit))
	}
	if order != nil {
		urlValues.Add("order", *order)
	}
	if after != nil {
		urlValues.Add("after", *after)
	}
	if before != nil {
		urlValues.Add("before", *before)
	}

	encodedValues := ""
	if len(urlValues) > 0 {
		encodedValues = "?" + urlValues.Encode()
	}

	urlSuffix := fmt.Sprintf("%s%s", assistantsSuffix, encodedValues)
	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

// CreateAssistantFile creates a new assistant file.
func (c *Client) CreateAssistantFile(
	ctx context.Context,
	assistantID string,
	request AssistantFileRequest,
) (response AssistantFile, err error) {
	urlSuffix := fmt.Sprintf("%s/%s%s", assistantsSuffix, assistantID, assistantsFilesSuffix)
	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix),
		withBody(request),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

// RetrieveAssistantFile retrieves an assistant file.
func (c *Client) RetrieveAssistantFile(
	ctx context.Context,
	assistantID string,
	fileID string,
) (response AssistantFile, err error) {
	urlSuffix := fmt.Sprintf("%s/%s%s/%s", assistantsSuffix, assistantID, assistantsFilesSuffix, fileID)
	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

// DeleteAssistantFile deletes an existing file.
func (c *Client) DeleteAssistantFile(
	ctx context.Context,
	assistantID string,
	fileID string,
) (err error) {
	urlSuffix := fmt.Sprintf("%s/%s%s/%s", assistantsSuffix, assistantID, assistantsFilesSuffix, fileID)
	req, err := c.newRequest(ctx, http.MethodDelete, c.fullURL(urlSuffix),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, nil)
	return
}

// ListAssistantFiles Lists the currently available files for an assistant.
func (c *Client) ListAssistantFiles(
	ctx context.Context,
	assistantID string,
	limit *int,
	order *string,
	after *string,
	before *string,
) (response AssistantFilesList, err error) {
	urlValues := url.Values{}
	if limit != nil {
		urlValues.Add("limit", fmt.Sprintf("%d", *limit))
	}
	if order != nil {
		urlValues.Add("order", *order)
	}
	if after != nil {
		urlValues.Add("after", *after)
	}
	if before != nil {
		urlValues.Add("before", *before)
	}

	encodedValues := ""
	if len(urlValues) > 0 {
		encodedValues = "?" + urlValues.Encode()
	}

	urlSuffix := fmt.Sprintf("%s/%s%s%s", assistantsSuffix, assistantID, assistantsFilesSuffix, encodedValues)
	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
		withBetaAssistantVersion(c.config.AssistantVersion))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}


================================================
FILE: assistant_test.go
================================================
package openai_test

import (
	"context"

	openai "github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/internal/test/checks"

	"encoding/json"
	"fmt"
	"net/http"
	"testing"
)

// TestAssistant Tests the assistant endpoint of the API using the mocked server.
func TestAssistant(t *testing.T) {
	assistantID := "asst_abc123"
	assistantName := "Ambrogio"
	assistantDescription := "Ambrogio is a friendly assistant."
	assistantInstructions := `You are a personal math tutor. 
When asked a question, write and run Python code to answer the question.`
	assistantFileID := "file-wB6RM6wHdA49HfS2DJ9fEyrH"
	limit := 20
	order := "desc"
	after := "asst_abc122"
	before := "asst_abc124"

	client, server, teardown := setupOpenAITestServer()
	defer teardown()

	server.RegisterHandler(
		"/v1/assistants/"+assistantID+"/files/"+assistantFileID,
		func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodGet {
				resBytes, _ := json.Marshal(openai.AssistantFile{
					ID:          assistantFileID,
					Object:      "assistant.file",
					CreatedAt:   1234567890,
					AssistantID: assistantID,
				})
				fmt.Fprintln(w, string(resBytes))
			} else if r.Method == http.MethodDelete {
				fmt.Fprintln(w, `{
					id: "file-wB6RM6wHdA49HfS2DJ9fEyrH",
					object: "assistant.file.deleted",
					deleted: true
				  }`)
			}
		},
	)

	server.RegisterHandler(
		"/v1/assistants/"+assistantID+"/files",
		func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodGet {
				resBytes, _ := json.Marshal(openai.AssistantFilesList{
					AssistantFiles: []openai.AssistantFile{
						{
							ID:          assistantFileID,
							Object:      "assistant.file",
							CreatedAt:   1234567890,
							AssistantID: assistantID,
						},
					},
				})
				fmt.Fprintln(w, string(resBytes))
			} else if r.Method == http.MethodPost {
				var request openai.AssistantFileRequest
				err := json.NewDecoder(r.Body).Decode(&request)
				checks.NoError(t, err, "Decode error")

				resBytes, _ := json.Marshal(openai.AssistantFile{
					ID:          request.FileID,
					Object:      "assistant.file",
					CreatedAt:   1234567890,
					AssistantID: assistantID,
				})
				fmt.Fprintln(w, string(resBytes))
			}
		},
	)

	server.RegisterHandler(
		"/v1/assistants/"+assistantID,
		func(w http.ResponseWriter, r *http.Request) {
			switch r.Method {
			case http.MethodGet:
				resBytes, _ := json.Marshal(openai.Assistant{
					ID:           assistantID,
					Object:       "assistant",
					CreatedAt:    1234567890,
					Name:         &assistantName,
					Model:        openai.GPT4TurboPreview,
					Description:  &assistantDescription,
					Instructions: &assistantInstructions,
				})
				fmt.Fprintln(w, string(resBytes))
			case http.MethodPost:
				var request openai.Assistant
				err := json.NewDecoder(r.Body).Decode(&request)
				checks.NoError(t, err, "Decode error")

				resBytes, _ := json.Marshal(openai.Assistant{
					ID:           assistantID,
					Object:       "assistant",
					CreatedAt:    1234567890,
					Name:         request.Name,
					Model:        request.Model,
					Description:  request.Description,
					Instructions: request.Instructions,
					Tools:        request.Tools,
				})
				fmt.Fprintln(w, string(resBytes))
			case http.MethodDelete:
				fmt.Fprintln(w, `{
					"id": "asst_abc123",
					"object": "assistant.deleted",
					"deleted": true
				  }`)
			}
		},
	)

	server.RegisterHandler(
		"/v1/assistants",
		func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodPost {
				var request openai.AssistantRequest
				err := json.NewDecoder(r.Body).Decode(&request)
				checks.NoError(t, err, "Decode error")

				resBytes, _ := json.Marshal(openai.Assistant{
					ID:           assistantID,
					Object:       "assistant",
					CreatedAt:    1234567890,
					Name:         request.Name,
					Model:        request.Model,
					Description:  request.Description,
					Instructions: request.Instructions,
					Tools:        request.Tools,
				})
				fmt.Fprintln(w, string(resBytes))
			} else if r.Method == http.MethodGet {
				resBytes, _ := json.Marshal(openai.AssistantsList{
					LastID:  &assistantID,
					FirstID: &assistantID,
					Assistants: []openai.Assistant{
						{
							ID:           assistantID,
							Object:       "assistant",
							CreatedAt:    1234567890,
							Name:         &assistantName,
							Model:        openai.GPT4TurboPreview,
							Description:  &assistantDescription,
							Instructions: &assistantInstructions,
						},
					},
				})
				fmt.Fprintln(w, string(resBytes))
			}
		},
	)

	ctx := context.Background()

	t.Run("create_assistant", func(t *testing.T) {
		_, err := client.CreateAssistant(ctx, openai.AssistantRequest{
			Name:         &assistantName,
			Description:  &assistantDescription,
			Model:        openai.GPT4TurboPreview,
			Instructions: &assistantInstructions,
		})
		checks.NoError(t, err, "CreateAssistant error")
	})

	t.Run("retrieve_assistant", func(t *testing.T) {
		_, err := client.RetrieveAssistant(ctx, assistantID)
		checks.NoError(t, err, "RetrieveAssistant error")
	})

	t.Run("delete_assistant", func(t *testing.T) {
		_, err := client.DeleteAssistant(ctx, assistantID)
		checks.NoError(t, err, "DeleteAssistant error")
	})

	t.Run("list_assistant", func(t *testing.T) {
		_, err := client.ListAssistants(ctx, &limit, &order, &after, &before)
		checks.NoError(t, err, "ListAssistants error")
	})

	t.Run("create_assistant_file", func(t *testing.T) {
		_, err := client.CreateAssistantFile(ctx, assistantID, openai.AssistantFileRequest{
			FileID: assistantFileID,
		})
		checks.NoError(t, err, "CreateAssistantFile error")
	})

	t.Run("list_assistant_files", func(t *testing.T) {
		_, err := client.ListAssistantFiles(ctx, assistantID, &limit, &order, &after, &before)
		checks.NoError(t, err, "ListAssistantFiles error")
	})

	t.Run("retrieve_assistant_file", func(t *testing.T) {
		_, err := client.RetrieveAssistantFile(ctx, assistantID, assistantFileID)
		checks.NoError(t, err, "RetrieveAssistantFile error")
	})

	t.Run("delete_assistant_file", func(t *testing.T) {
		err := client.DeleteAssistantFile(ctx, assistantID, assistantFileID)
		checks.NoError(t, err, "DeleteAssistantFile error")
	})

	t.Run("modify_assistant_no_tools", func(t *testing.T) {
		assistant, err := client.ModifyAssistant(ctx, assistantID, openai.AssistantRequest{
			Name:         &assistantName,
			Description:  &assistantDescription,
			Model:        openai.GPT4TurboPreview,
			Instructions: &assistantInstructions,
		})
		checks.NoError(t, err, "ModifyAssistant error")

		if assistant.Tools != nil {
			t.Errorf("expected nil got %v", assistant.Tools)
		}
	})

	t.Run("modify_assistant_with_tools", func(t *testing.T) {
		assistant, err := client.ModifyAssistant(ctx, assistantID, openai.AssistantRequest{
			Name:         &assistantName,
			Description:  &assistantDescription,
			Model:        openai.GPT4TurboPreview,
			Instructions: &assistantInstructions,
			Tools:        []openai.AssistantTool{{Type: openai.AssistantToolTypeFunction}},
		})
		checks.NoError(t, err, "ModifyAssistant error")

		if assistant.Tools == nil || len(assistant.Tools) != 1 {
			t.Errorf("expected a slice got %v", assistant.Tools)
		}
	})

	t.Run("modify_assistant_empty_tools", func(t *testing.T) {
		assistant, err := client.ModifyAssistant(ctx, assistantID, openai.AssistantRequest{
			Name:         &assistantName,
			Description:  &assistantDescription,
			Model:        openai.GPT4TurboPreview,
			Instructions: &assistantInstructions,
			Tools:        make([]openai.AssistantTool, 0),
		})

		checks.NoError(t, err, "ModifyAssistant error")

		if assistant.Tools == nil {
			t.Errorf("expected a slice got %v", assistant.Tools)
		}
	})
}

func TestAzureAssistant(t *testing.T) {
	assistantID := "asst_abc123"
	assistantName := "Ambrogio"
	assistantDescription := "Ambrogio is a friendly assistant."
	assistantInstructions := `You are a personal math tutor. 
When asked a question, write and run Python code to answer the question.`
	assistantFileID := "file-wB6RM6wHdA49HfS2DJ9fEyrH"
	limit := 20
	order := "desc"
	after := "asst_abc122"
	before := "asst_abc124"

	client, server, teardown := setupAzureTestServer()
	defer teardown()

	server.RegisterHandler(
		"/openai/assistants/"+assistantID+"/files/"+assistantFileID,
		func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodGet {
				resBytes, _ := json.Marshal(openai.AssistantFile{
					ID:          assistantFileID,
					Object:      "assistant.file",
					CreatedAt:   1234567890,
					AssistantID: assistantID,
				})
				fmt.Fprintln(w, string(resBytes))
			} else if r.Method == http.MethodDelete {
				fmt.Fprintln(w, `{
					id: "file-wB6RM6wHdA49HfS2DJ9fEyrH",
					object: "assistant.file.deleted",
					deleted: true
				  }`)
			}
		},
	)

	server.RegisterHandler(
		"/openai/assistants/"+assistantID+"/files",
		func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodGet {
				resBytes, _ := json.Marshal(openai.AssistantFilesList{
					AssistantFiles: []openai.AssistantFile{
						{
							ID:          assistantFileID,
							Object:      "assistant.file",
							CreatedAt:   1234567890,
							AssistantID: assistantID,
						},
					},
				})
				fmt.Fprintln(w, string(resBytes))
			} else if r.Method == http.MethodPost {
				var request openai.AssistantFileRequest
				err := json.NewDecoder(r.Body).Decode(&request)
				checks.NoError(t, err, "Decode error")

				resBytes, _ := json.Marshal(openai.AssistantFile{
					ID:          request.FileID,
					Object:      "assistant.file",
					CreatedAt:   1234567890,
					AssistantID: assistantID,
				})
				fmt.Fprintln(w, string(resBytes))
			}
		},
	)

	server.RegisterHandler(
		"/openai/assistants/"+assistantID,
		func(w http.ResponseWriter, r *http.Request) {
			switch r.Method {
			case http.MethodGet:
				resBytes, _ := json.Marshal(openai.Assistant{
					ID:           assistantID,
					Object:       "assistant",
					CreatedAt:    1234567890,
					Name:         &assistantName,
					Model:        openai.GPT4TurboPreview,
					Description:  &assistantDescription,
					Instructions: &assistantInstructions,
				})
				fmt.Fprintln(w, string(resBytes))
			case http.MethodPost:
				var request openai.AssistantRequest
				err := json.NewDecoder(r.Body).Decode(&request)
				checks.NoError(t, err, "Decode error")

				resBytes, _ := json.Marshal(openai.Assistant{
					ID:           assistantID,
					Object:       "assistant",
					CreatedAt:    1234567890,
					Name:         request.Name,
					Model:        request.Model,
					Description:  request.Description,
					Instructions: request.Instructions,
					Tools:        request.Tools,
				})
				fmt.Fprintln(w, string(resBytes))
			case http.MethodDelete:
				fmt.Fprintln(w, `{
					"id": "asst_abc123",
					"object": "assistant.deleted",
					"deleted": true
				  }`)
			}
		},
	)

	server.RegisterHandler(
		"/openai/assistants",
		func(w http.ResponseWriter, r *http.Request) {
			if r.Method == http.MethodPost {
				var request openai.AssistantRequest
				err := json.NewDecoder(r.Body).Decode(&request)
				checks.NoError(t, err, "Decode error")

				resBytes, _ := json.Marshal(openai.Assistant{
					ID:           assistantID,
					Object:       "assistant",
					CreatedAt:    1234567890,
					Name:         request.Name,
					Model:        request.Model,
					Description:  request.Description,
					Instructions: request.Instructions,
					Tools:        request.Tools,
				})
				fmt.Fprintln(w, string(resBytes))
			} else if r.Method == http.MethodGet {
				resBytes, _ := json.Marshal(openai.AssistantsList{
					LastID:  &assistantID,
					FirstID: &assistantID,
					Assistants: []openai.Assistant{
						{
							ID:           assistantID,
							Object:       "assistant",
							CreatedAt:    1234567890,
							Name:         &assistantName,
							Model:        openai.GPT4TurboPreview,
							Description:  &assistantDescription,
							Instructions: &assistantInstructions,
						},
					},
				})
				fmt.Fprintln(w, string(resBytes))
			}
		},
	)

	ctx := context.Background()

	_, err := client.CreateAssistant(ctx, openai.AssistantRequest{
		Name:         &assistantName,
		Description:  &assistantDescription,
		Model:        openai.GPT4TurboPreview,
		Instructions: &assistantInstructions,
	})
	checks.NoError(t, err, "CreateAssistant error")

	_, err = client.RetrieveAssistant(ctx, assistantID)
	checks.NoError(t, err, "RetrieveAssistant error")

	_, err = client.ModifyAssistant(ctx, assistantID, openai.AssistantRequest{
		Name:         &assistantName,
		Description:  &assistantDescription,
		Model:        openai.GPT4TurboPreview,
		Instructions: &assistantInstructions,
	})
	checks.NoError(t, err, "ModifyAssistant error")

	_, err = client.DeleteAssistant(ctx, assistantID)
	checks.NoError(t, err, "DeleteAssistant error")

	_, err = client.ListAssistants(ctx, &limit, &order, &after, &before)
	checks.NoError(t, err, "ListAssistants error")

	_, err = client.CreateAssistantFile(ctx, assistantID, openai.AssistantFileRequest{
		FileID: assistantFileID,
	})
	checks.NoError(t, err, "CreateAssistantFile error")

	_, err = client.ListAssistantFiles(ctx, assistantID, &limit, &order, &after, &before)
	checks.NoError(t, err, "ListAssistantFiles error")

	_, err = client.RetrieveAssistantFile(ctx, assistantID, assistantFileID)
	checks.NoError(t, err, "RetrieveAssistantFile error")

	err = client.DeleteAssistantFile(ctx, assistantID, assistantFileID)
	checks.NoError(t, err, "DeleteAssistantFile error")
}


================================================
FILE: audio.go
================================================
package openai

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"os"

	utils "github.com/sashabaranov/go-openai/internal"
)

// Whisper Defines the models provided by OpenAI to use when processing audio with OpenAI.
const (
	Whisper1 = "whisper-1"
)

// Response formats; Whisper uses AudioResponseFormatJSON by default.
type AudioResponseFormat string

const (
	AudioResponseFormatJSON        AudioResponseFormat = "json"
	AudioResponseFormatText        AudioResponseFormat = "text"
	AudioResponseFormatSRT         AudioResponseFormat = "srt"
	AudioResponseFormatVerboseJSON AudioResponseFormat = "verbose_json"
	AudioResponseFormatVTT         AudioResponseFormat = "vtt"
)

type TranscriptionTimestampGranularity string

const (
	TranscriptionTimestampGranularityWord    TranscriptionTimestampGranularity = "word"
	TranscriptionTimestampGranularitySegment TranscriptionTimestampGranularity = "segment"
)

// AudioRequest represents a request structure for audio API.
type AudioRequest struct {
	Model string

	// FilePath is either an existing file in your filesystem or a filename representing the contents of Reader.
	FilePath string

	// Reader is an optional io.Reader when you do not want to use an existing file.
	Reader io.Reader

	Prompt                 string
	Temperature            float32
	Language               string // Only for transcription.
	Format                 AudioResponseFormat
	TimestampGranularities []TranscriptionTimestampGranularity // Only for transcription.
}

// AudioResponse represents a response structure for audio API.
type AudioResponse struct {
	Task     string  `json:"task"`
	Language string  `json:"language"`
	Duration float64 `json:"duration"`
	Segments []struct {
		ID               int     `json:"id"`
		Seek             int     `json:"seek"`
		Start            float64 `json:"start"`
		End              float64 `json:"end"`
		Text             string  `json:"text"`
		Tokens           []int   `json:"tokens"`
		Temperature      float64 `json:"temperature"`
		AvgLogprob       float64 `json:"avg_logprob"`
		CompressionRatio float64 `json:"compression_ratio"`
		NoSpeechProb     float64 `json:"no_speech_prob"`
		Transient        bool    `json:"transient"`
	} `json:"segments"`
	Words []struct {
		Word  string  `json:"word"`
		Start float64 `json:"start"`
		End   float64 `json:"end"`
	} `json:"words"`
	Text string `json:"text"`

	httpHeader
}

type audioTextResponse struct {
	Text string `json:"text"`

	httpHeader
}

func (r *audioTextResponse) ToAudioResponse() AudioResponse {
	return AudioResponse{
		Text:       r.Text,
		httpHeader: r.httpHeader,
	}
}

// CreateTranscription — API call to create a transcription. Returns transcribed text.
func (c *Client) CreateTranscription(
	ctx context.Context,
	request AudioRequest,
) (response AudioResponse, err error) {
	return c.callAudioAPI(ctx, request, "transcriptions")
}

// CreateTranslation — API call to translate audio into English.
func (c *Client) CreateTranslation(
	ctx context.Context,
	request AudioRequest,
) (response AudioResponse, err error) {
	return c.callAudioAPI(ctx, request, "translations")
}

// callAudioAPI — API call to an audio endpoint.
func (c *Client) callAudioAPI(
	ctx context.Context,
	request AudioRequest,
	endpointSuffix string,
) (response AudioResponse, err error) {
	var formBody bytes.Buffer
	builder := c.createFormBuilder(&formBody)

	if err = audioMultipartForm(request, builder); err != nil {
		return AudioResponse{}, err
	}

	urlSuffix := fmt.Sprintf("/audio/%s", endpointSuffix)
	req, err := c.newRequest(
		ctx,
		http.MethodPost,
		c.fullURL(urlSuffix, withModel(request.Model)),
		withBody(&formBody),
		withContentType(builder.FormDataContentType()),
	)
	if err != nil {
		return AudioResponse{}, err
	}

	if request.HasJSONResponse() {
		err = c.sendRequest(req, &response)
	} else {
		var textResponse audioTextResponse
		err = c.sendRequest(req, &textResponse)
		response = textResponse.ToAudioResponse()
	}
	if err != nil {
		return AudioResponse{}, err
	}
	return
}

// HasJSONResponse returns true if the response format is JSON.
func (r AudioRequest) HasJSONResponse() bool {
	return r.Format == "" || r.Format == AudioResponseFormatJSON || r.Format == AudioResponseFormatVerboseJSON
}

// audioMultipartForm creates a form with audio file contents and the name of the model to use for
// audio processing.
func audioMultipartForm(request AudioRequest, b utils.FormBuilder) error {
	err := createFileField(request, b)
	if err != nil {
		return err
	}

	err = b.WriteField("model", request.Model)
	if err != nil {
		return fmt.Errorf("writing model name: %w", err)
	}

	// Create a form field for the prompt (if provided)
	if request.Prompt != "" {
		err = b.WriteField("prompt", request.Prompt)
		if err != nil {
			return fmt.Errorf("writing prompt: %w", err)
		}
	}

	// Create a form field for the format (if provided)
	if request.Format != "" {
		err = b.WriteField("response_format", string(request.Format))
		if err != nil {
			return fmt.Errorf("writing format: %w", err)
		}
	}

	// Create a form field for the temperature (if provided)
	if request.Temperature != 0 {
		err = b.WriteField("temperature", fmt.Sprintf("%.2f", request.Temperature))
		if err != nil {
			return fmt.Errorf("writing temperature: %w", err)
		}
	}

	// Create a form field for the language (if provided)
	if request.Language != "" {
		err = b.WriteField("language", request.Language)
		if err != nil {
			return fmt.Errorf("writing language: %w", err)
		}
	}

	if len(request.TimestampGranularities) > 0 {
		for _, tg := range request.TimestampGranularities {
			err = b.WriteField("timestamp_granularities[]", string(tg))
			if err != nil {
				return fmt.Errorf("writing timestamp_granularities[]: %w", err)
			}
		}
	}

	// Close the multipart writer
	return b.Close()
}

// createFileField creates the "file" form field from either an existing file or by using the reader.
func createFileField(request AudioRequest, b utils.FormBuilder) error {
	if request.Reader != nil {
		err := b.CreateFormFileReader("file", request.Reader, request.FilePath)
		if err != nil {
			return fmt.Errorf("creating form using reader: %w", err)
		}
		return nil
	}

	f, err := os.Open(request.FilePath)
	if err != nil {
		return fmt.Errorf("opening audio file: %w", err)
	}
	defer f.Close()

	err = b.CreateFormFile("file", f)
	if err != nil {
		return fmt.Errorf("creating form file: %w", err)
	}

	return nil
}


================================================
FILE: audio_api_test.go
================================================
package openai_test

import (
	"bytes"
	"context"
	"errors"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"path/filepath"
	"strings"
	"testing"

	"github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/internal/test"
	"github.com/sashabaranov/go-openai/internal/test/checks"
)

// TestAudio Tests the transcription and translation endpoints of the API using the mocked server.
func TestAudio(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/audio/transcriptions", handleAudioEndpoint)
	server.RegisterHandler("/v1/audio/translations", handleAudioEndpoint)

	testcases := []struct {
		name     string
		createFn func(context.Context, openai.AudioRequest) (openai.AudioResponse, error)
	}{
		{
			"transcribe",
			client.CreateTranscription,
		},
		{
			"translate",
			client.CreateTranslation,
		},
	}

	ctx := context.Background()

	for _, tc := range testcases {
		t.Run(tc.name, func(t *testing.T) {
			path := filepath.Join(t.TempDir(), "fake.mp3")
			test.CreateTestFile(t, path)

			req := openai.AudioRequest{
				FilePath: path,
				Model:    "whisper-3",
			}
			_, err := tc.createFn(ctx, req)
			checks.NoError(t, err, "audio API error")
		})

		t.Run(tc.name+" (with reader)", func(t *testing.T) {
			req := openai.AudioRequest{
				FilePath: "fake.webm",
				Reader:   bytes.NewBuffer([]byte(`some webm binary data`)),
				Model:    "whisper-3",
			}
			_, err := tc.createFn(ctx, req)
			checks.NoError(t, err, "audio API error")
		})
	}
}

func TestAudioWithOptionalArgs(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/audio/transcriptions", handleAudioEndpoint)
	server.RegisterHandler("/v1/audio/translations", handleAudioEndpoint)

	testcases := []struct {
		name     string
		createFn func(context.Context, openai.AudioRequest) (openai.AudioResponse, error)
	}{
		{
			"transcribe",
			client.CreateTranscription,
		},
		{
			"translate",
			client.CreateTranslation,
		},
	}

	ctx := context.Background()

	for _, tc := range testcases {
		t.Run(tc.name, func(t *testing.T) {
			path := filepath.Join(t.TempDir(), "fake.mp3")
			test.CreateTestFile(t, path)

			req := openai.AudioRequest{
				FilePath:    path,
				Model:       "whisper-3",
				Prompt:      "用简体中文",
				Temperature: 0.5,
				Language:    "zh",
				Format:      openai.AudioResponseFormatSRT,
				TimestampGranularities: []openai.TranscriptionTimestampGranularity{
					openai.TranscriptionTimestampGranularitySegment,
					openai.TranscriptionTimestampGranularityWord,
				},
			}
			_, err := tc.createFn(ctx, req)
			checks.NoError(t, err, "audio API error")
		})
	}
}

// handleAudioEndpoint Handles the completion endpoint by the test server.
func handleAudioEndpoint(w http.ResponseWriter, r *http.Request) {
	var err error

	// audio endpoints only accept POST requests
	if r.Method != "POST" {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
	}

	mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, "failed to parse media type", http.StatusBadRequest)
		return
	}

	if !strings.HasPrefix(mediaType, "multipart") {
		http.Error(w, "request is not multipart", http.StatusBadRequest)
	}

	boundary, ok := params["boundary"]
	if !ok {
		http.Error(w, "no boundary in params", http.StatusBadRequest)
		return
	}

	fileData := &bytes.Buffer{}
	mr := multipart.NewReader(r.Body, boundary)
	part, err := mr.NextPart()
	if err != nil && errors.Is(err, io.EOF) {
		http.Error(w, "error accessing file", http.StatusBadRequest)
		return
	}
	if _, err = io.Copy(fileData, part); err != nil {
		http.Error(w, "failed to copy file", http.StatusInternalServerError)
		return
	}

	if len(fileData.Bytes()) == 0 {
		w.WriteHeader(http.StatusInternalServerError)
		http.Error(w, "received empty file data", http.StatusBadRequest)
		return
	}

	if _, err = w.Write([]byte(`{"body": "hello"}`)); err != nil {
		http.Error(w, "failed to write body", http.StatusInternalServerError)
		return
	}
}


================================================
FILE: audio_test.go
================================================
package openai //nolint:testpackage // testing private field

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"testing"

	utils "github.com/sashabaranov/go-openai/internal"
	"github.com/sashabaranov/go-openai/internal/test"
	"github.com/sashabaranov/go-openai/internal/test/checks"
)

func TestAudioWithFailingFormBuilder(t *testing.T) {
	path := filepath.Join(t.TempDir(), "fake.mp3")
	test.CreateTestFile(t, path)

	req := AudioRequest{
		FilePath:    path,
		Prompt:      "test",
		Temperature: 0.5,
		Language:    "en",
		Format:      AudioResponseFormatSRT,
		TimestampGranularities: []TranscriptionTimestampGranularity{
			TranscriptionTimestampGranularitySegment,
			TranscriptionTimestampGranularityWord,
		},
	}

	mockFailedErr := fmt.Errorf("mock form builder fail")
	mockBuilder := &mockFormBuilder{}

	mockBuilder.mockCreateFormFile = func(string, *os.File) error {
		return mockFailedErr
	}
	err := audioMultipartForm(req, mockBuilder)
	checks.ErrorIs(t, err, mockFailedErr, "audioMultipartForm should return error if form builder fails")

	mockBuilder.mockCreateFormFile = func(string, *os.File) error {
		return nil
	}

	var failForField string
	mockBuilder.mockWriteField = func(fieldname, _ string) error {
		if fieldname == failForField {
			return mockFailedErr
		}
		return nil
	}

	failOn := []string{"model", "prompt", "temperature", "language", "response_format", "timestamp_granularities[]"}
	for _, failingField := range failOn {
		failForField = failingField
		mockFailedErr = fmt.Errorf("mock form builder fail on field %s", failingField)

		err = audioMultipartForm(req, mockBuilder)
		checks.ErrorIs(t, err, mockFailedErr, "audioMultipartForm should return error if form builder fails")
	}
}

func TestCreateFileField(t *testing.T) {
	t.Run("createFileField failing file", func(t *testing.T) {
		path := filepath.Join(t.TempDir(), "fake.mp3")
		test.CreateTestFile(t, path)

		req := AudioRequest{
			FilePath: path,
		}

		mockFailedErr := fmt.Errorf("mock form builder fail")
		mockBuilder := &mockFormBuilder{
			mockCreateFormFile: func(string, *os.File) error {
				return mockFailedErr
			},
		}

		err := createFileField(req, mockBuilder)
		checks.ErrorIs(t, err, mockFailedErr, "createFileField using a file should return error if form builder fails")
	})

	t.Run("createFileField failing reader", func(t *testing.T) {
		req := AudioRequest{
			FilePath: "test.wav",
			Reader:   bytes.NewBuffer([]byte(`wav test contents`)),
		}

		mockFailedErr := fmt.Errorf("mock form builder fail")
		mockBuilder := &mockFormBuilder{
			mockCreateFormFileReader: func(string, io.Reader, string) error {
				return mockFailedErr
			},
		}

		err := createFileField(req, mockBuilder)
		checks.ErrorIs(t, err, mockFailedErr, "createFileField using a reader should return error if form builder fails")
	})

	t.Run("createFileField failing open", func(t *testing.T) {
		req := AudioRequest{
			FilePath: "non_existing_file.wav",
		}

		mockBuilder := &mockFormBuilder{}

		err := createFileField(req, mockBuilder)
		checks.HasError(t, err, "createFileField using file should return error when open file fails")
	})
}

// failingFormBuilder always returns an error when creating form files.
type failingFormBuilder struct{ err error }

func (f *failingFormBuilder) CreateFormFile(_ string, _ *os.File) error {
	return f.err
}

func (f *failingFormBuilder) CreateFormFileReader(_ string, _ io.Reader, _ string) error {
	return f.err
}

func (f *failingFormBuilder) WriteField(_, _ string) error {
	return nil
}

func (f *failingFormBuilder) Close() error {
	return nil
}

func (f *failingFormBuilder) FormDataContentType() string {
	return "multipart/form-data"
}

// failingAudioRequestBuilder simulates an error during HTTP request construction.
type failingAudioRequestBuilder struct{ err error }

func (f *failingAudioRequestBuilder) Build(
	_ context.Context,
	_, _ string,
	_ any,
	_ http.Header,
) (*http.Request, error) {
	return nil, f.err
}

// errorHTTPClient always returns an error when making HTTP calls.
type errorHTTPClient struct{ err error }

func (e *errorHTTPClient) Do(_ *http.Request) (*http.Response, error) {
	return nil, e.err
}

func TestCallAudioAPIMultipartFormError(t *testing.T) {
	client := NewClient("test-token")
	errForm := errors.New("mock create form file failure")
	// Override form builder to force an error during multipart form creation.
	client.createFormBuilder = func(_ io.Writer) utils.FormBuilder {
		return &failingFormBuilder{err: errForm}
	}

	// Provide a reader so createFileField uses the reader path (no file open).
	req := AudioRequest{FilePath: "fake.mp3", Reader: bytes.NewBuffer([]byte("dummy")), Model: Whisper1}
	_, err := client.callAudioAPI(context.Background(), req, "transcriptions")
	if err == nil {
		t.Fatal("expected error but got none")
	}
	if !errors.Is(err, errForm) {
		t.Errorf("expected error %v, got %v", errForm, err)
	}
}

func TestCallAudioAPINewRequestError(t *testing.T) {
	client := NewClient("test-token")
	// Create a real temp file so multipart form succeeds.
	tmp := t.TempDir()
	path := filepath.Join(tmp, "file.mp3")
	if err := os.WriteFile(path, []byte("content"), 0644); err != nil {
		t.Fatalf("failed to write temp file: %v", err)
	}

	errBuild := errors.New("mock build failure")
	client.requestBuilder = &failingAudioRequestBuilder{err: errBuild}

	req := AudioRequest{FilePath: path, Model: Whisper1}
	_, err := client.callAudioAPI(context.Background(), req, "translations")
	if err == nil {
		t.Fatal("expected error but got none")
	}
	if !errors.Is(err, errBuild) {
		t.Errorf("expected error %v, got %v", errBuild, err)
	}
}

func TestCallAudioAPISendRequestErrorJSON(t *testing.T) {
	client := NewClient("test-token")
	// Create a real temp file so multipart form succeeds.
	tmp := t.TempDir()
	path := filepath.Join(tmp, "file.mp3")
	if err := os.WriteFile(path, []byte("content"), 0644); err != nil {
		t.Fatalf("failed to write temp file: %v", err)
	}

	errHTTP := errors.New("mock HTTPClient failure")
	// Override HTTP client to simulate a network error.
	client.config.HTTPClient = &errorHTTPClient{err: errHTTP}

	req := AudioRequest{FilePath: path, Model: Whisper1}
	_, err := client.callAudioAPI(context.Background(), req, "transcriptions")
	if err == nil {
		t.Fatal("expected error but got none")
	}
	if !errors.Is(err, errHTTP) {
		t.Errorf("expected error %v, got %v", errHTTP, err)
	}
}

func TestCallAudioAPISendRequestErrorText(t *testing.T) {
	client := NewClient("test-token")
	tmp := t.TempDir()
	path := filepath.Join(tmp, "file.mp3")
	if err := os.WriteFile(path, []byte("content"), 0644); err != nil {
		t.Fatalf("failed to write temp file: %v", err)
	}

	errHTTP := errors.New("mock HTTPClient failure")
	client.config.HTTPClient = &errorHTTPClient{err: errHTTP}

	// Use a non-JSON response format to exercise the text path.
	req := AudioRequest{FilePath: path, Model: Whisper1, Format: AudioResponseFormatText}
	_, err := client.callAudioAPI(context.Background(), req, "translations")
	if err == nil {
		t.Fatal("expected error but got none")
	}
	if !errors.Is(err, errHTTP) {
		t.Errorf("expected error %v, got %v", errHTTP, err)
	}
}


================================================
FILE: batch.go
================================================
package openai

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

const batchesSuffix = "/batches"

type BatchEndpoint string

const (
	BatchEndpointChatCompletions BatchEndpoint = "/v1/chat/completions"
	BatchEndpointCompletions     BatchEndpoint = "/v1/completions"
	BatchEndpointEmbeddings      BatchEndpoint = "/v1/embeddings"
)

type BatchLineItem interface {
	MarshalBatchLineItem() []byte
}

type BatchChatCompletionRequest struct {
	CustomID string                `json:"custom_id"`
	Body     ChatCompletionRequest `json:"body"`
	Method   string                `json:"method"`
	URL      BatchEndpoint         `json:"url"`
}

func (r BatchChatCompletionRequest) MarshalBatchLineItem() []byte {
	marshal, _ := json.Marshal(r)
	return marshal
}

type BatchCompletionRequest struct {
	CustomID string            `json:"custom_id"`
	Body     CompletionRequest `json:"body"`
	Method   string            `json:"method"`
	URL      BatchEndpoint     `json:"url"`
}

func (r BatchCompletionRequest) MarshalBatchLineItem() []byte {
	marshal, _ := json.Marshal(r)
	return marshal
}

type BatchEmbeddingRequest struct {
	CustomID string           `json:"custom_id"`
	Body     EmbeddingRequest `json:"body"`
	Method   string           `json:"method"`
	URL      BatchEndpoint    `json:"url"`
}

func (r BatchEmbeddingRequest) MarshalBatchLineItem() []byte {
	marshal, _ := json.Marshal(r)
	return marshal
}

type Batch struct {
	ID       string        `json:"id"`
	Object   string        `json:"object"`
	Endpoint BatchEndpoint `json:"endpoint"`
	Errors   *struct {
		Object string `json:"object,omitempty"`
		Data   []struct {
			Code    string  `json:"code,omitempty"`
			Message string  `json:"message,omitempty"`
			Param   *string `json:"param,omitempty"`
			Line    *int    `json:"line,omitempty"`
		} `json:"data"`
	} `json:"errors"`
	InputFileID      string             `json:"input_file_id"`
	CompletionWindow string             `json:"completion_window"`
	Status           string             `json:"status"`
	OutputFileID     *string            `json:"output_file_id"`
	ErrorFileID      *string            `json:"error_file_id"`
	CreatedAt        int                `json:"created_at"`
	InProgressAt     *int               `json:"in_progress_at"`
	ExpiresAt        *int               `json:"expires_at"`
	FinalizingAt     *int               `json:"finalizing_at"`
	CompletedAt      *int               `json:"completed_at"`
	FailedAt         *int               `json:"failed_at"`
	ExpiredAt        *int               `json:"expired_at"`
	CancellingAt     *int               `json:"cancelling_at"`
	CancelledAt      *int               `json:"cancelled_at"`
	RequestCounts    BatchRequestCounts `json:"request_counts"`
	Metadata         map[string]any     `json:"metadata"`
}

type BatchRequestCounts struct {
	Total     int `json:"total"`
	Completed int `json:"completed"`
	Failed    int `json:"failed"`
}

type CreateBatchRequest struct {
	InputFileID      string         `json:"input_file_id"`
	Endpoint         BatchEndpoint  `json:"endpoint"`
	CompletionWindow string         `json:"completion_window"`
	Metadata         map[string]any `json:"metadata"`
}

type BatchResponse struct {
	httpHeader
	Batch
}

// CreateBatch — API call to Create batch.
func (c *Client) CreateBatch(
	ctx context.Context,
	request CreateBatchRequest,
) (response BatchResponse, err error) {
	if request.CompletionWindow == "" {
		request.CompletionWindow = "24h"
	}

	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(batchesSuffix), withBody(request))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}

type UploadBatchFileRequest struct {
	FileName string
	Lines    []BatchLineItem
}

func (r *UploadBatchFileRequest) MarshalJSONL() []byte {
	buff := bytes.Buffer{}
	for i, line := range r.Lines {
		if i != 0 {
			buff.Write([]byte("\n"))
		}
		buff.Write(line.MarshalBatchLineItem())
	}
	return buff.Bytes()
}

func (r *UploadBatchFileRequest) AddChatCompletion(customerID string, body ChatCompletionRequest) {
	r.Lines = append(r.Lines, BatchChatCompletionRequest{
		CustomID: customerID,
		Body:     body,
		Method:   "POST",
		URL:      BatchEndpointChatCompletions,
	})
}

func (r *UploadBatchFileRequest) AddCompletion(customerID string, body CompletionRequest) {
	r.Lines = append(r.Lines, BatchCompletionRequest{
		CustomID: customerID,
		Body:     body,
		Method:   "POST",
		URL:      BatchEndpointCompletions,
	})
}

func (r *UploadBatchFileRequest) AddEmbedding(customerID string, body EmbeddingRequest) {
	r.Lines = append(r.Lines, BatchEmbeddingRequest{
		CustomID: customerID,
		Body:     body,
		Method:   "POST",
		URL:      BatchEndpointEmbeddings,
	})
}

// UploadBatchFile — upload batch file.
func (c *Client) UploadBatchFile(ctx context.Context, request UploadBatchFileRequest) (File, error) {
	if request.FileName == "" {
		request.FileName = "@batchinput.jsonl"
	}
	return c.CreateFileBytes(ctx, FileBytesRequest{
		Name:    request.FileName,
		Bytes:   request.MarshalJSONL(),
		Purpose: PurposeBatch,
	})
}

type CreateBatchWithUploadFileRequest struct {
	Endpoint         BatchEndpoint  `json:"endpoint"`
	CompletionWindow string         `json:"completion_window"`
	Metadata         map[string]any `json:"metadata"`
	UploadBatchFileRequest
}

// CreateBatchWithUploadFile — API call to Create batch with upload file.
func (c *Client) CreateBatchWithUploadFile(
	ctx context.Context,
	request CreateBatchWithUploadFileRequest,
) (response BatchResponse, err error) {
	var file File
	file, err = c.UploadBatchFile(ctx, UploadBatchFileRequest{
		FileName: request.FileName,
		Lines:    request.Lines,
	})
	if err != nil {
		return
	}
	return c.CreateBatch(ctx, CreateBatchRequest{
		InputFileID:      file.ID,
		Endpoint:         request.Endpoint,
		CompletionWindow: request.CompletionWindow,
		Metadata:         request.Metadata,
	})
}

// RetrieveBatch — API call to Retrieve batch.
func (c *Client) RetrieveBatch(
	ctx context.Context,
	batchID string,
) (response BatchResponse, err error) {
	urlSuffix := fmt.Sprintf("%s/%s", batchesSuffix, batchID)
	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix))
	if err != nil {
		return
	}
	err = c.sendRequest(req, &response)
	return
}

// CancelBatch — API call to Cancel batch.
func (c *Client) CancelBatch(
	ctx context.Context,
	batchID string,
) (response BatchResponse, err error) {
	urlSuffix := fmt.Sprintf("%s/%s/cancel", batchesSuffix, batchID)
	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix))
	if err != nil {
		return
	}
	err = c.sendRequest(req, &response)
	return
}

type ListBatchResponse struct {
	httpHeader
	Object  string  `json:"object"`
	Data    []Batch `json:"data"`
	FirstID string  `json:"first_id"`
	LastID  string  `json:"last_id"`
	HasMore bool    `json:"has_more"`
}

// ListBatch API call to List batch.
func (c *Client) ListBatch(ctx context.Context, after *string, limit *int) (response ListBatchResponse, err error) {
	urlValues := url.Values{}
	if limit != nil {
		urlValues.Add("limit", fmt.Sprintf("%d", *limit))
	}
	if after != nil {
		urlValues.Add("after", *after)
	}
	encodedValues := ""
	if len(urlValues) > 0 {
		encodedValues = "?" + urlValues.Encode()
	}

	urlSuffix := fmt.Sprintf("%s%s", batchesSuffix, encodedValues)
	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix))
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}


================================================
FILE: batch_test.go
================================================
package openai_test

import (
	"context"
	"fmt"
	"net/http"
	"reflect"
	"testing"

	"github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/internal/test/checks"
)

func TestUploadBatchFile(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()

	server.RegisterHandler("/v1/files", handleCreateFile)
	req := openai.UploadBatchFileRequest{}
	req.AddChatCompletion("req-1", openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	_, err := client.UploadBatchFile(context.Background(), req)
	checks.NoError(t, err, "UploadBatchFile error")
}

func TestCreateBatch(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()

	server.RegisterHandler("/v1/batches", handleBatchEndpoint)
	_, err := client.CreateBatch(context.Background(), openai.CreateBatchRequest{
		InputFileID:      "file-abc",
		Endpoint:         openai.BatchEndpointChatCompletions,
		CompletionWindow: "24h",
	})
	checks.NoError(t, err, "CreateBatch error")
}

func TestCreateBatchWithUploadFile(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/files", handleCreateFile)
	server.RegisterHandler("/v1/batches", handleBatchEndpoint)
	req := openai.CreateBatchWithUploadFileRequest{
		Endpoint: openai.BatchEndpointChatCompletions,
	}
	req.AddChatCompletion("req-1", openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	_, err := client.CreateBatchWithUploadFile(context.Background(), req)
	checks.NoError(t, err, "CreateBatchWithUploadFile error")
}

func TestRetrieveBatch(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/batches/file-id-1", handleRetrieveBatchEndpoint)
	_, err := client.RetrieveBatch(context.Background(), "file-id-1")
	checks.NoError(t, err, "RetrieveBatch error")
}

func TestCancelBatch(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/batches/file-id-1/cancel", handleCancelBatchEndpoint)
	_, err := client.CancelBatch(context.Background(), "file-id-1")
	checks.NoError(t, err, "RetrieveBatch error")
}

func TestListBatch(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/batches", handleBatchEndpoint)
	after := "batch_abc123"
	limit := 10
	_, err := client.ListBatch(context.Background(), &after, &limit)
	checks.NoError(t, err, "RetrieveBatch error")
}

func TestUploadBatchFileRequest_AddChatCompletion(t *testing.T) {
	type args struct {
		customerID string
		body       openai.ChatCompletionRequest
	}
	tests := []struct {
		name string
		args []args
		want []byte
	}{
		{"", []args{
			{
				customerID: "req-1",
				body: openai.ChatCompletionRequest{
					MaxTokens: 5,
					Model:     openai.GPT3Dot5Turbo,
					Messages: []openai.ChatCompletionMessage{
						{
							Role:    openai.ChatMessageRoleUser,
							Content: "Hello!",
						},
					},
				},
			},
			{
				customerID: "req-2",
				body: openai.ChatCompletionRequest{
					MaxTokens: 5,
					Model:     openai.GPT3Dot5Turbo,
					Messages: []openai.ChatCompletionMessage{
						{
							Role:    openai.ChatMessageRoleUser,
							Content: "Hello!",
						},
					},
				},
			},
		}, []byte("{\"custom_id\":\"req-1\",\"body\":{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"user\",\"content\":\"Hello!\"}],\"max_tokens\":5},\"method\":\"POST\",\"url\":\"/v1/chat/completions\"}\n{\"custom_id\":\"req-2\",\"body\":{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"user\",\"content\":\"Hello!\"}],\"max_tokens\":5},\"method\":\"POST\",\"url\":\"/v1/chat/completions\"}")}, //nolint:lll
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &openai.UploadBatchFileRequest{}
			for _, arg := range tt.args {
				r.AddChatCompletion(arg.customerID, arg.body)
			}
			got := r.MarshalJSONL()
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Marshal() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestUploadBatchFileRequest_AddCompletion(t *testing.T) {
	type args struct {
		customerID string
		body       openai.CompletionRequest
	}
	tests := []struct {
		name string
		args []args
		want []byte
	}{
		{"", []args{
			{
				customerID: "req-1",
				body: openai.CompletionRequest{
					Model: openai.GPT3Dot5Turbo,
					User:  "Hello",
				},
			},
			{
				customerID: "req-2",
				body: openai.CompletionRequest{
					Model: openai.GPT3Dot5Turbo,
					User:  "Hello",
				},
			},
		}, []byte("{\"custom_id\":\"req-1\",\"body\":{\"model\":\"gpt-3.5-turbo\",\"user\":\"Hello\"},\"method\":\"POST\",\"url\":\"/v1/completions\"}\n{\"custom_id\":\"req-2\",\"body\":{\"model\":\"gpt-3.5-turbo\",\"user\":\"Hello\"},\"method\":\"POST\",\"url\":\"/v1/completions\"}")}, //nolint:lll
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &openai.UploadBatchFileRequest{}
			for _, arg := range tt.args {
				r.AddCompletion(arg.customerID, arg.body)
			}
			got := r.MarshalJSONL()
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Marshal() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestUploadBatchFileRequest_AddEmbedding(t *testing.T) {
	type args struct {
		customerID string
		body       openai.EmbeddingRequest
	}
	tests := []struct {
		name string
		args []args
		want []byte
	}{
		{"", []args{
			{
				customerID: "req-1",
				body: openai.EmbeddingRequest{
					Model: openai.GPT3Dot5Turbo,
					Input: []string{"Hello", "World"},
				},
			},
			{
				customerID: "req-2",
				body: openai.EmbeddingRequest{
					Model: openai.AdaEmbeddingV2,
					Input: []string{"Hello", "World"},
				},
			},
		}, []byte("{\"custom_id\":\"req-1\",\"body\":{\"input\":[\"Hello\",\"World\"],\"model\":\"gpt-3.5-turbo\"},\"method\":\"POST\",\"url\":\"/v1/embeddings\"}\n{\"custom_id\":\"req-2\",\"body\":{\"input\":[\"Hello\",\"World\"],\"model\":\"text-embedding-ada-002\"},\"method\":\"POST\",\"url\":\"/v1/embeddings\"}")}, //nolint:lll
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &openai.UploadBatchFileRequest{}
			for _, arg := range tt.args {
				r.AddEmbedding(arg.customerID, arg.body)
			}
			got := r.MarshalJSONL()
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Marshal() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func handleBatchEndpoint(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodPost {
		_, _ = fmt.Fprintln(w, `{
			  "id": "batch_abc123",
			  "object": "batch",
			  "endpoint": "/v1/completions",
			  "errors": null,
			  "input_file_id": "file-abc123",
			  "completion_window": "24h",
			  "status": "completed",
			  "output_file_id": "file-cvaTdG",
			  "error_file_id": "file-HOWS94",
			  "created_at": 1711471533,
			  "in_progress_at": 1711471538,
			  "expires_at": 1711557933,
			  "finalizing_at": 1711493133,
			  "completed_at": 1711493163,
			  "failed_at": null,
			  "expired_at": null,
			  "cancelling_at": null,
			  "cancelled_at": null,
			  "request_counts": {
				"total": 100,
				"completed": 95,
				"failed": 5
			  },
			  "metadata": {
				"customer_id": "user_123456789",
				"batch_description": "Nightly eval job"
			  }
			}`)
	} else if r.Method == http.MethodGet {
		_, _ = fmt.Fprintln(w, `{
			  "object": "list",
			  "data": [
				{
				  "id": "batch_abc123",
				  "object": "batch",
				  "endpoint": "/v1/chat/completions",
				  "errors": null,
				  "input_file_id": "file-abc123",
				  "completion_window": "24h",
				  "status": "completed",
				  "output_file_id": "file-cvaTdG",
				  "error_file_id": "file-HOWS94",
				  "created_at": 1711471533,
				  "in_progress_at": 1711471538,
				  "expires_at": 1711557933,
				  "finalizing_at": 1711493133,
				  "completed_at": 1711493163,
				  "failed_at": null,
				  "expired_at": null,
				  "cancelling_at": null,
				  "cancelled_at": null,
				  "request_counts": {
					"total": 100,
					"completed": 95,
					"failed": 5
				  },
				  "metadata": {
					"customer_id": "user_123456789",
					"batch_description": "Nightly job"
				  }
				}
			  ],
			  "first_id": "batch_abc123",
			  "last_id": "batch_abc456",
			  "has_more": true
			}`)
	}
}

func handleRetrieveBatchEndpoint(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodGet {
		_, _ = fmt.Fprintln(w, `{
		  "id": "batch_abc123",
		  "object": "batch",
		  "endpoint": "/v1/completions",
		  "errors": null,
		  "input_file_id": "file-abc123",
		  "completion_window": "24h",
		  "status": "completed",
		  "output_file_id": "file-cvaTdG",
		  "error_file_id": "file-HOWS94",
		  "created_at": 1711471533,
		  "in_progress_at": 1711471538,
		  "expires_at": 1711557933,
		  "finalizing_at": 1711493133,
		  "completed_at": 1711493163,
		  "failed_at": null,
		  "expired_at": null,
		  "cancelling_at": null,
		  "cancelled_at": null,
		  "request_counts": {
			"total": 100,
			"completed": 95,
			"failed": 5
		  },
		  "metadata": {
			"customer_id": "user_123456789",
			"batch_description": "Nightly eval job"
		  }
		}`)
	}
}

func handleCancelBatchEndpoint(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodPost {
		_, _ = fmt.Fprintln(w, `{
		  "id": "batch_abc123",
		  "object": "batch",
		  "endpoint": "/v1/chat/completions",
		  "errors": null,
		  "input_file_id": "file-abc123",
		  "completion_window": "24h",
		  "status": "cancelling",
		  "output_file_id": null,
		  "error_file_id": null,
		  "created_at": 1711471533,
		  "in_progress_at": 1711471538,
		  "expires_at": 1711557933,
		  "finalizing_at": null,
		  "completed_at": null,
		  "failed_at": null,
		  "expired_at": null,
		  "cancelling_at": 1711475133,
		  "cancelled_at": null,
		  "request_counts": {
			"total": 100,
			"completed": 23,
			"failed": 1
		  },
		  "metadata": {
			"customer_id": "user_123456789",
			"batch_description": "Nightly eval job"
		  }
		}`)
	}
}


================================================
FILE: chat.go
================================================
package openai

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"

	"github.com/sashabaranov/go-openai/jsonschema"
)

// Chat message role defined by the OpenAI API.
const (
	ChatMessageRoleSystem    = "system"
	ChatMessageRoleUser      = "user"
	ChatMessageRoleAssistant = "assistant"
	ChatMessageRoleFunction  = "function"
	ChatMessageRoleTool      = "tool"
	ChatMessageRoleDeveloper = "developer"
)

const chatCompletionsSuffix = "/chat/completions"

var (
	ErrChatCompletionInvalidModel       = errors.New("this model is not supported with this method, please use CreateCompletion client method instead") //nolint:lll
	ErrChatCompletionStreamNotSupported = errors.New("streaming is not supported with this method, please use CreateChatCompletionStream")              //nolint:lll
	ErrContentFieldsMisused             = errors.New("can't use both Content and MultiContent properties simultaneously")
)

type Hate struct {
	Filtered bool   `json:"filtered"`
	Severity string `json:"severity,omitempty"`
}
type SelfHarm struct {
	Filtered bool   `json:"filtered"`
	Severity string `json:"severity,omitempty"`
}
type Sexual struct {
	Filtered bool   `json:"filtered"`
	Severity string `json:"severity,omitempty"`
}
type Violence struct {
	Filtered bool   `json:"filtered"`
	Severity string `json:"severity,omitempty"`
}

type JailBreak struct {
	Filtered bool `json:"filtered"`
	Detected bool `json:"detected"`
}

type Profanity struct {
	Filtered bool `json:"filtered"`
	Detected bool `json:"detected"`
}

type ContentFilterResults struct {
	Hate      Hate      `json:"hate,omitempty"`
	SelfHarm  SelfHarm  `json:"self_harm,omitempty"`
	Sexual    Sexual    `json:"sexual,omitempty"`
	Violence  Violence  `json:"violence,omitempty"`
	JailBreak JailBreak `json:"jailbreak,omitempty"`
	Profanity Profanity `json:"profanity,omitempty"`
}

type PromptAnnotation struct {
	PromptIndex          int                  `json:"prompt_index,omitempty"`
	ContentFilterResults ContentFilterResults `json:"content_filter_results,omitempty"`
}

type ImageURLDetail string

const (
	ImageURLDetailHigh ImageURLDetail = "high"
	ImageURLDetailLow  ImageURLDetail = "low"
	ImageURLDetailAuto ImageURLDetail = "auto"
)

type ChatMessageImageURL struct {
	URL    string         `json:"url,omitempty"`
	Detail ImageURLDetail `json:"detail,omitempty"`
}

type ChatMessagePartType string

const (
	ChatMessagePartTypeText     ChatMessagePartType = "text"
	ChatMessagePartTypeImageURL ChatMessagePartType = "image_url"
)

type ChatMessagePart struct {
	Type     ChatMessagePartType  `json:"type,omitempty"`
	Text     string               `json:"text,omitempty"`
	ImageURL *ChatMessageImageURL `json:"image_url,omitempty"`
}

type ChatCompletionMessage struct {
	Role         string `json:"role"`
	Content      string `json:"content,omitempty"`
	Refusal      string `json:"refusal,omitempty"`
	MultiContent []ChatMessagePart

	// This property isn't in the official documentation, but it's in
	// the documentation for the official library for python:
	// - https://github.com/openai/openai-python/blob/main/chatml.md
	// - https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
	Name string `json:"name,omitempty"`

	// This property is used for the "reasoning" feature supported by deepseek-reasoner
	// which is not in the official documentation.
	// the doc from deepseek:
	// - https://api-docs.deepseek.com/api/create-chat-completion#responses
	ReasoningContent string `json:"reasoning_content,omitempty"`

	FunctionCall *FunctionCall `json:"function_call,omitempty"`

	// For Role=assistant prompts this may be set to the tool calls generated by the model, such as function calls.
	ToolCalls []ToolCall `json:"tool_calls,omitempty"`

	// For Role=tool prompts this should be set to the ID given in the assistant's prior request to call a tool.
	ToolCallID string `json:"tool_call_id,omitempty"`
}

func (m ChatCompletionMessage) MarshalJSON() ([]byte, error) {
	if m.Content != "" && m.MultiContent != nil {
		return nil, ErrContentFieldsMisused
	}
	if len(m.MultiContent) > 0 {
		msg := struct {
			Role             string            `json:"role"`
			Content          string            `json:"-"`
			Refusal          string            `json:"refusal,omitempty"`
			MultiContent     []ChatMessagePart `json:"content,omitempty"`
			Name             string            `json:"name,omitempty"`
			ReasoningContent string            `json:"reasoning_content,omitempty"`
			FunctionCall     *FunctionCall     `json:"function_call,omitempty"`
			ToolCalls        []ToolCall        `json:"tool_calls,omitempty"`
			ToolCallID       string            `json:"tool_call_id,omitempty"`
		}(m)
		return json.Marshal(msg)
	}

	msg := struct {
		Role             string            `json:"role"`
		Content          string            `json:"content,omitempty"`
		Refusal          string            `json:"refusal,omitempty"`
		MultiContent     []ChatMessagePart `json:"-"`
		Name             string            `json:"name,omitempty"`
		ReasoningContent string            `json:"reasoning_content,omitempty"`
		FunctionCall     *FunctionCall     `json:"function_call,omitempty"`
		ToolCalls        []ToolCall        `json:"tool_calls,omitempty"`
		ToolCallID       string            `json:"tool_call_id,omitempty"`
	}(m)
	return json.Marshal(msg)
}

func (m *ChatCompletionMessage) UnmarshalJSON(bs []byte) error {
	msg := struct {
		Role             string `json:"role"`
		Content          string `json:"content"`
		Refusal          string `json:"refusal,omitempty"`
		MultiContent     []ChatMessagePart
		Name             string        `json:"name,omitempty"`
		ReasoningContent string        `json:"reasoning_content,omitempty"`
		FunctionCall     *FunctionCall `json:"function_call,omitempty"`
		ToolCalls        []ToolCall    `json:"tool_calls,omitempty"`
		ToolCallID       string        `json:"tool_call_id,omitempty"`
	}{}

	if err := json.Unmarshal(bs, &msg); err == nil {
		*m = ChatCompletionMessage(msg)
		return nil
	}
	multiMsg := struct {
		Role             string `json:"role"`
		Content          string
		Refusal          string            `json:"refusal,omitempty"`
		MultiContent     []ChatMessagePart `json:"content"`
		Name             string            `json:"name,omitempty"`
		ReasoningContent string            `json:"reasoning_content,omitempty"`
		FunctionCall     *FunctionCall     `json:"function_call,omitempty"`
		ToolCalls        []ToolCall        `json:"tool_calls,omitempty"`
		ToolCallID       string            `json:"tool_call_id,omitempty"`
	}{}
	if err := json.Unmarshal(bs, &multiMsg); err != nil {
		return err
	}
	*m = ChatCompletionMessage(multiMsg)
	return nil
}

type ToolCall struct {
	// Index is not nil only in chat completion chunk object
	Index    *int         `json:"index,omitempty"`
	ID       string       `json:"id,omitempty"`
	Type     ToolType     `json:"type"`
	Function FunctionCall `json:"function"`
}

type FunctionCall struct {
	Name string `json:"name,omitempty"`
	// call function with arguments in JSON format
	Arguments string `json:"arguments,omitempty"`
}

type ChatCompletionResponseFormatType string

const (
	ChatCompletionResponseFormatTypeJSONObject ChatCompletionResponseFormatType = "json_object"
	ChatCompletionResponseFormatTypeJSONSchema ChatCompletionResponseFormatType = "json_schema"
	ChatCompletionResponseFormatTypeText       ChatCompletionResponseFormatType = "text"
)

type ChatCompletionResponseFormat struct {
	Type       ChatCompletionResponseFormatType        `json:"type,omitempty"`
	JSONSchema *ChatCompletionResponseFormatJSONSchema `json:"json_schema,omitempty"`
}

type ChatCompletionResponseFormatJSONSchema struct {
	Name        string         `json:"name"`
	Description string         `json:"description,omitempty"`
	Schema      json.Marshaler `json:"schema"`
	Strict      bool           `json:"strict"`
}

func (r *ChatCompletionResponseFormatJSONSchema) UnmarshalJSON(data []byte) error {
	type rawJSONSchema struct {
		Name        string          `json:"name"`
		Description string          `json:"description,omitempty"`
		Schema      json.RawMessage `json:"schema"`
		Strict      bool            `json:"strict"`
	}
	var raw rawJSONSchema
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	r.Name = raw.Name
	r.Description = raw.Description
	r.Strict = raw.Strict
	if len(raw.Schema) > 0 && string(raw.Schema) != "null" {
		var d jsonschema.Definition
		err := json.Unmarshal(raw.Schema, &d)
		if err != nil {
			return err
		}
		r.Schema = &d
	}
	return nil
}

// ChatCompletionRequestExtensions contains third-party OpenAI API extensions
// (e.g., vendor-specific implementations like vLLM).
type ChatCompletionRequestExtensions struct {
	// GuidedChoice is a vLLM-specific extension that restricts the model's output
	// to one of the predefined string choices provided in this field. This feature
	// is used to constrain the model's responses to a controlled set of options,
	// ensuring predictable and consistent outputs in scenarios where specific
	// choices are required.
	GuidedChoice []string `json:"guided_choice,omitempty"`
}

// ChatCompletionRequest represents a request structure for chat completion API.
type ChatCompletionRequest struct {
	Model    string                  `json:"model"`
	Messages []ChatCompletionMessage `json:"messages"`
	// MaxTokens The maximum number of tokens that can be generated in the chat completion.
	// This value can be used to control costs for text generated via API.
	// Deprecated: use MaxCompletionTokens. Not compatible with o1-series models.
	// refs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-max_tokens
	MaxTokens int `json:"max_tokens,omitempty"`
	// MaxCompletionTokens An upper bound for the number of tokens that can be generated for a completion,
	// including visible output tokens and reasoning tokens https://platform.openai.com/docs/guides/reasoning
	MaxCompletionTokens int                           `json:"max_completion_tokens,omitempty"`
	Temperature         float32                       `json:"temperature,omitempty"`
	TopP                float32                       `json:"top_p,omitempty"`
	N                   int                           `json:"n,omitempty"`
	Stream              bool                          `json:"stream,omitempty"`
	Stop                []string                      `json:"stop,omitempty"`
	PresencePenalty     float32                       `json:"presence_penalty,omitempty"`
	ResponseFormat      *ChatCompletionResponseFormat `json:"response_format,omitempty"`
	Seed                *int                          `json:"seed,omitempty"`
	FrequencyPenalty    float32                       `json:"frequency_penalty,omitempty"`
	// LogitBias is must be a token id string (specified by their token ID in the tokenizer), not a word string.
	// incorrect: `"logit_bias":{"You": 6}`, correct: `"logit_bias":{"1639": 6}`
	// refs: https://platform.openai.com/docs/api-reference/chat/create#chat/create-logit_bias
	LogitBias map[string]int `json:"logit_bias,omitempty"`
	// LogProbs indicates whether to return log probabilities of the output tokens or not.
	// If true, returns the log probabilities of each output token returned in the content of message.
	// This option is currently not available on the gpt-4-vision-preview model.
	LogProbs bool `json:"logprobs,omitempty"`
	// TopLogProbs is an integer between 0 and 5 specifying the number of most likely tokens to return at each
	// token position, each with an associated log probability.
	// logprobs must be set to true if this parameter is used.
	TopLogProbs int    `json:"top_logprobs,omitempty"`
	User        string `json:"user,omitempty"`
	// Deprecated: use Tools instead.
	Functions []FunctionDefinition `json:"functions,omitempty"`
	// Deprecated: use ToolChoice instead.
	FunctionCall any    `json:"function_call,omitempty"`
	Tools        []Tool `json:"tools,omitempty"`
	// This can be either a string or an ToolChoice object.
	ToolChoice any `json:"tool_choice,omitempty"`
	// Options for streaming response. Only set this when you set stream: true.
	StreamOptions *StreamOptions `json:"stream_options,omitempty"`
	// Disable the default behavior of parallel tool calls by setting it: false.
	ParallelToolCalls any `json:"parallel_tool_calls,omitempty"`
	// Store can be set to true to store the output of this completion request for use in distillations and evals.
	// https://platform.openai.com/docs/api-reference/chat/create#chat-create-store
	Store bool `json:"store,omitempty"`
	// Controls effort on reasoning for reasoning models. It can be set to "low", "medium", or "high".
	ReasoningEffort string `json:"reasoning_effort,omitempty"`
	// Metadata to store with the completion.
	Metadata map[string]string `json:"metadata,omitempty"`
	// Configuration for a predicted output.
	Prediction *Prediction `json:"prediction,omitempty"`
	// ChatTemplateKwargs provides a way to add non-standard parameters to the request body.
	// Additional kwargs to pass to the template renderer. Will be accessible by the chat template.
	// Such as think mode for qwen3. "chat_template_kwargs": {"enable_thinking": false}
	// https://qwen.readthedocs.io/en/latest/deployment/vllm.html#thinking-non-thinking-modes
	ChatTemplateKwargs map[string]any `json:"chat_template_kwargs,omitempty"`
	// Specifies the latency tier to use for processing the request.
	ServiceTier ServiceTier `json:"service_tier,omitempty"`
	// Verbosity determines how many output tokens are generated. Lowering the number of
	// tokens reduces overall latency. It can be set to "low", "medium", or "high".
	// Note: This field is only confirmed to work with gpt-5, gpt-5-mini and gpt-5-nano.
	// Also, it is not in the API reference of chat completion at the time of writing,
	// though it is supported by the API.
	Verbosity string `json:"verbosity,omitempty"`
	// A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.
	// The IDs should be a string that uniquely identifies each user.
	// We recommend hashing their username or email address, in order to avoid sending us any identifying information.
	// https://platform.openai.com/docs/api-reference/chat/create#chat_create-safety_identifier
	SafetyIdentifier string `json:"safety_identifier,omitempty"`
	// Embedded struct for non-OpenAI extensions
	ChatCompletionRequestExtensions
}

type StreamOptions struct {
	// If set, an additional chunk will be streamed before the data: [DONE] message.
	// The usage field on this chunk shows the token usage statistics for the entire request,
	// and the choices field will always be an empty array.
	// All other chunks will also include a usage field, but with a null value.
	IncludeUsage bool `json:"include_usage,omitempty"`
}

type ToolType string

const (
	ToolTypeFunction ToolType = "function"
)

type Tool struct {
	Type     ToolType            `json:"type"`
	Function *FunctionDefinition `json:"function,omitempty"`
}

type ToolChoice struct {
	Type     ToolType     `json:"type"`
	Function ToolFunction `json:"function,omitempty"`
}

type ToolFunction struct {
	Name string `json:"name"`
}

type FunctionDefinition struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Strict      bool   `json:"strict,omitempty"`
	// Parameters is an object describing the function.
	// You can pass json.RawMessage to describe the schema,
	// or you can pass in a struct which serializes to the proper JSON schema.
	// The jsonschema package is provided for convenience, but you should
	// consider another specialized library if you require more complex schemas.
	Parameters any `json:"parameters"`
}

// Deprecated: use FunctionDefinition instead.
type FunctionDefine = FunctionDefinition

type TopLogProbs struct {
	Token   string  `json:"token"`
	LogProb float64 `json:"logprob"`
	Bytes   []byte  `json:"bytes,omitempty"`
}

// LogProb represents the probability information for a token.
type LogProb struct {
	Token   string  `json:"token"`
	LogProb float64 `json:"logprob"`
	Bytes   []byte  `json:"bytes,omitempty"` // Omitting the field if it is null
	// TopLogProbs is a list of the most likely tokens and their log probability, at this token position.
	// In rare cases, there may be fewer than the number of requested top_logprobs returned.
	TopLogProbs []TopLogProbs `json:"top_logprobs"`
}

// LogProbs is the top-level structure containing the log probability information.
type LogProbs struct {
	// Content is a list of message content tokens with log probability information.
	Content []LogProb `json:"content"`
}

type Prediction struct {
	Content string `json:"content"`
	Type    string `json:"type"`
}

type FinishReason string

const (
	FinishReasonStop          FinishReason = "stop"
	FinishReasonLength        FinishReason = "length"
	FinishReasonFunctionCall  FinishReason = "function_call"
	FinishReasonToolCalls     FinishReason = "tool_calls"
	FinishReasonContentFilter FinishReason = "content_filter"
	FinishReasonNull          FinishReason = "null"
)

type ServiceTier string

const (
	ServiceTierAuto     ServiceTier = "auto"
	ServiceTierDefault  ServiceTier = "default"
	ServiceTierFlex     ServiceTier = "flex"
	ServiceTierPriority ServiceTier = "priority"
)

func (r FinishReason) MarshalJSON() ([]byte, error) {
	if r == FinishReasonNull || r == "" {
		return []byte("null"), nil
	}
	return []byte(`"` + string(r) + `"`), nil // best effort to not break future API changes
}

type ChatCompletionChoice struct {
	Index   int                   `json:"index"`
	Message ChatCompletionMessage `json:"message"`
	// FinishReason
	// stop: API returned complete message,
	// or a message terminated by one of the stop sequences provided via the stop parameter
	// length: Incomplete model output due to max_tokens parameter or token limit
	// function_call: The model decided to call a function
	// content_filter: Omitted content due to a flag from our content filters
	// null: API response still in progress or incomplete
	FinishReason         FinishReason         `json:"finish_reason"`
	LogProbs             *LogProbs            `json:"logprobs,omitempty"`
	ContentFilterResults ContentFilterResults `json:"content_filter_results,omitempty"`
}

// ChatCompletionResponse represents a response structure for chat completion API.
type ChatCompletionResponse struct {
	ID                  string                 `json:"id"`
	Object              string                 `json:"object"`
	Created             int64                  `json:"created"`
	Model               string                 `json:"model"`
	Choices             []ChatCompletionChoice `json:"choices"`
	Usage               Usage                  `json:"usage"`
	SystemFingerprint   string                 `json:"system_fingerprint"`
	PromptFilterResults []PromptFilterResult   `json:"prompt_filter_results,omitempty"`
	ServiceTier         ServiceTier            `json:"service_tier,omitempty"`

	httpHeader
}

// CreateChatCompletion — API call to Create a completion for the chat message.
func (c *Client) CreateChatCompletion(
	ctx context.Context,
	request ChatCompletionRequest,
) (response ChatCompletionResponse, err error) {
	if request.Stream {
		err = ErrChatCompletionStreamNotSupported
		return
	}

	urlSuffix := chatCompletionsSuffix
	if !checkEndpointSupportsModel(urlSuffix, request.Model) {
		err = ErrChatCompletionInvalidModel
		return
	}

	reasoningValidator := NewReasoningValidator()
	if err = reasoningValidator.Validate(request); err != nil {
		return
	}

	req, err := c.newRequest(
		ctx,
		http.MethodPost,
		c.fullURL(urlSuffix, withModel(request.Model)),
		withBody(request),
	)
	if err != nil {
		return
	}

	err = c.sendRequest(req, &response)
	return
}


================================================
FILE: chat_stream.go
================================================
package openai

import (
	"context"
	"net/http"
)

type ChatCompletionStreamChoiceDelta struct {
	Content      string        `json:"content,omitempty"`
	Role         string        `json:"role,omitempty"`
	FunctionCall *FunctionCall `json:"function_call,omitempty"`
	ToolCalls    []ToolCall    `json:"tool_calls,omitempty"`
	Refusal      string        `json:"refusal,omitempty"`

	// This property is used for the "reasoning" feature supported by deepseek-reasoner
	// which is not in the official documentation.
	// the doc from deepseek:
	// - https://api-docs.deepseek.com/api/create-chat-completion#responses
	ReasoningContent string `json:"reasoning_content,omitempty"`
}

type ChatCompletionStreamChoiceLogprobs struct {
	Content []ChatCompletionTokenLogprob `json:"content,omitempty"`
	Refusal []ChatCompletionTokenLogprob `json:"refusal,omitempty"`
}

type ChatCompletionTokenLogprob struct {
	Token       string                                 `json:"token"`
	Bytes       []int64                                `json:"bytes,omitempty"`
	Logprob     float64                                `json:"logprob,omitempty"`
	TopLogprobs []ChatCompletionTokenLogprobTopLogprob `json:"top_logprobs"`
}

type ChatCompletionTokenLogprobTopLogprob struct {
	Token   string  `json:"token"`
	Bytes   []int64 `json:"bytes"`
	Logprob float64 `json:"logprob"`
}

type ChatCompletionStreamChoice struct {
	Index                int                                 `json:"index"`
	Delta                ChatCompletionStreamChoiceDelta     `json:"delta"`
	Logprobs             *ChatCompletionStreamChoiceLogprobs `json:"logprobs,omitempty"`
	FinishReason         FinishReason                        `json:"finish_reason"`
	ContentFilterResults ContentFilterResults                `json:"content_filter_results,omitempty"`
}

type PromptFilterResult struct {
	Index                int                  `json:"index"`
	ContentFilterResults ContentFilterResults `json:"content_filter_results,omitempty"`
}

type ChatCompletionStreamResponse struct {
	ID                  string                       `json:"id"`
	Object              string                       `json:"object"`
	Created             int64                        `json:"created"`
	Model               string                       `json:"model"`
	Choices             []ChatCompletionStreamChoice `json:"choices"`
	SystemFingerprint   string                       `json:"system_fingerprint"`
	PromptAnnotations   []PromptAnnotation           `json:"prompt_annotations,omitempty"`
	PromptFilterResults []PromptFilterResult         `json:"prompt_filter_results,omitempty"`
	// An optional field that will only be present when you set stream_options: {"include_usage": true} in your request.
	// When present, it contains a null value except for the last chunk which contains the token usage statistics
	// for the entire request.
	Usage *Usage `json:"usage,omitempty"`
}

// ChatCompletionStream
// Note: Perhaps it is more elegant to abstract Stream using generics.
type ChatCompletionStream struct {
	*streamReader[ChatCompletionStreamResponse]
}

// CreateChatCompletionStream — API call to create a chat completion w/ streaming
// support. It sets whether to stream back partial progress. If set, tokens will be
// sent as data-only server-sent events as they become available, with the
// stream terminated by a data: [DONE] message.
func (c *Client) CreateChatCompletionStream(
	ctx context.Context,
	request ChatCompletionRequest,
) (stream *ChatCompletionStream, err error) {
	urlSuffix := chatCompletionsSuffix
	if !checkEndpointSupportsModel(urlSuffix, request.Model) {
		err = ErrChatCompletionInvalidModel
		return
	}

	request.Stream = true
	reasoningValidator := NewReasoningValidator()
	if err = reasoningValidator.Validate(request); err != nil {
		return
	}

	req, err := c.newRequest(
		ctx,
		http.MethodPost,
		c.fullURL(urlSuffix, withModel(request.Model)),
		withBody(request),
	)
	if err != nil {
		return nil, err
	}

	resp, err := sendRequestStream[ChatCompletionStreamResponse](c, req)
	if err != nil {
		return
	}
	stream = &ChatCompletionStream{
		streamReader: resp,
	}
	return
}


================================================
FILE: chat_stream_test.go
================================================
package openai_test

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"testing"

	"github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/internal/test/checks"
)

func TestChatCompletionsStreamWrongModel(t *testing.T) {
	config := openai.DefaultConfig("whatever")
	config.BaseURL = "http://localhost/v1"
	client := openai.NewClientWithConfig(config)
	ctx := context.Background()

	req := openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     "ada",
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	}
	_, err := client.CreateChatCompletionStream(ctx, req)
	if !errors.Is(err, openai.ErrChatCompletionInvalidModel) {
		t.Fatalf("CreateChatCompletion should return ErrChatCompletionInvalidModel, but returned: %v", err)
	}
}

func TestCreateChatCompletionStream(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")

		// Send test responses
		dataBytes := []byte{}
		dataBytes = append(dataBytes, []byte("event: message\n")...)
		//nolint:lll
		data := `{"id":"1","object":"completion","created":1598069254,"model":"gpt-3.5-turbo","system_fingerprint": "fp_d9767fc5b9","choices":[{"index":0,"delta":{"content":"response1"},"finish_reason":"max_tokens"}]}`
		dataBytes = append(dataBytes, []byte("data: "+data+"\n\n")...)

		dataBytes = append(dataBytes, []byte("event: message\n")...)
		//nolint:lll
		data = `{"id":"2","object":"completion","created":1598069255,"model":"gpt-3.5-turbo","system_fingerprint": "fp_d9767fc5b9","choices":[{"index":0,"delta":{"content":"response2"},"finish_reason":"max_tokens"}]}`
		dataBytes = append(dataBytes, []byte("data: "+data+"\n\n")...)

		dataBytes = append(dataBytes, []byte("event: done\n")...)
		dataBytes = append(dataBytes, []byte("data: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	expectedResponses := []openai.ChatCompletionStreamResponse{
		{
			ID:                "1",
			Object:            "completion",
			Created:           1598069254,
			Model:             openai.GPT3Dot5Turbo,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: "response1",
					},
					FinishReason: "max_tokens",
				},
			},
		},
		{
			ID:                "2",
			Object:            "completion",
			Created:           1598069255,
			Model:             openai.GPT3Dot5Turbo,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: "response2",
					},
					FinishReason: "max_tokens",
				},
			},
		},
	}

	for ix, expectedResponse := range expectedResponses {
		b, _ := json.Marshal(expectedResponse)
		t.Logf("%d: %s", ix, string(b))

		receivedResponse, streamErr := stream.Recv()
		checks.NoError(t, streamErr, "stream.Recv() failed")
		if !compareChatResponses(expectedResponse, receivedResponse) {
			t.Errorf("Stream response %v is %v, expected %v", ix, receivedResponse, expectedResponse)
		}
	}

	_, streamErr := stream.Recv()
	if !errors.Is(streamErr, io.EOF) {
		t.Errorf("stream.Recv() did not return EOF in the end: %v", streamErr)
	}

	_, streamErr = stream.Recv()

	checks.ErrorIs(t, streamErr, io.EOF, "stream.Recv() did not return EOF when the stream is finished")
	if !errors.Is(streamErr, io.EOF) {
		t.Errorf("stream.Recv() did not return EOF when the stream is finished: %v", streamErr)
	}
}

func TestCreateChatCompletionStreamError(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")

		// Send test responses
		dataBytes := []byte{}
		dataStr := []string{
			`{`,
			`"error": {`,
			`"message": "Incorrect API key provided: sk-***************************************",`,
			`"type": "invalid_request_error",`,
			`"param": null,`,
			`"code": "invalid_api_key"`,
			`}`,
			`}`,
		}
		for _, str := range dataStr {
			dataBytes = append(dataBytes, []byte(str+"\n")...)
		}

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	_, streamErr := stream.Recv()
	checks.HasError(t, streamErr, "stream.Recv() did not return error")

	var apiErr *openai.APIError
	if !errors.As(streamErr, &apiErr) {
		t.Errorf("stream.Recv() did not return APIError")
	}
	t.Logf("%+v\n", apiErr)
}

func TestCreateChatCompletionStreamWithHeaders(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.Header().Set(xCustomHeader, xCustomHeaderValue)

		// Send test responses
		//nolint:lll
		dataBytes := []byte(`data: {"error":{"message":"The server had an error while processing your request. Sorry about that!", "type":"server_ error", "param":null,"code":null}}`)
		dataBytes = append(dataBytes, []byte("\n\ndata: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	value := stream.Header().Get(xCustomHeader)
	if value != xCustomHeaderValue {
		t.Errorf("expected %s to be %s", xCustomHeaderValue, value)
	}
}

func TestCreateChatCompletionStreamWithRatelimitHeaders(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		for k, v := range rateLimitHeaders {
			switch val := v.(type) {
			case int:
				w.Header().Set(k, strconv.Itoa(val))
			default:
				w.Header().Set(k, fmt.Sprintf("%s", v))
			}
		}

		// Send test responses
		//nolint:lll
		dataBytes := []byte(`data: {"error":{"message":"The server had an error while processing your request. Sorry about that!", "type":"server_ error", "param":null,"code":null}}`)
		dataBytes = append(dataBytes, []byte("\n\ndata: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	headers := stream.GetRateLimitHeaders()
	bs1, _ := json.Marshal(headers)
	bs2, _ := json.Marshal(rateLimitHeaders)
	if string(bs1) != string(bs2) {
		t.Errorf("expected rate limit header %s to be %s", bs2, bs1)
	}
}

func TestCreateChatCompletionStreamErrorWithDataPrefix(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")

		// Send test responses
		//nolint:lll
		dataBytes := []byte(`data: {"error":{"message":"The server had an error while processing your request. Sorry about that!", "type":"server_ error", "param":null,"code":null}}`)
		dataBytes = append(dataBytes, []byte("\n\ndata: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	_, streamErr := stream.Recv()
	checks.HasError(t, streamErr, "stream.Recv() did not return error")

	var apiErr *openai.APIError
	if !errors.As(streamErr, &apiErr) {
		t.Errorf("stream.Recv() did not return APIError")
	}
	t.Logf("%+v\n", apiErr)
}

func TestCreateChatCompletionStreamRateLimitError(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(429)

		// Send test responses
		dataBytes := []byte(`{"error":{` +
			`"message": "You are sending requests too quickly.",` +
			`"type":"rate_limit_reached",` +
			`"param":null,` +
			`"code":"rate_limit_reached"}}`)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})
	_, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	var apiErr *openai.APIError
	if !errors.As(err, &apiErr) {
		t.Errorf("TestCreateChatCompletionStreamRateLimitError did not return APIError")
	}
	t.Logf("%+v\n", apiErr)
}

func TestCreateChatCompletionStreamWithRefusal(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")

		dataBytes := []byte{}

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"1","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"2","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{"refusal":"Hello"},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"3","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{"refusal":" World"},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"4","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		dataBytes = append(dataBytes, []byte("data: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 2000,
		Model:     openai.GPT4oMini20240718,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	expectedResponses := []openai.ChatCompletionStreamResponse{
		{
			ID:                "1",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{},
				},
			},
		},
		{
			ID:                "2",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Refusal: "Hello",
					},
				},
			},
		},
		{
			ID:                "3",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Refusal: " World",
					},
				},
			},
		},
		{
			ID:                "4",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index:        0,
					FinishReason: "stop",
				},
			},
		},
	}

	for ix, expectedResponse := range expectedResponses {
		b, _ := json.Marshal(expectedResponse)
		t.Logf("%d: %s", ix, string(b))

		receivedResponse, streamErr := stream.Recv()
		checks.NoError(t, streamErr, "stream.Recv() failed")
		if !compareChatResponses(expectedResponse, receivedResponse) {
			t.Errorf("Stream response %v is %v, expected %v", ix, receivedResponse, expectedResponse)
		}
	}

	_, streamErr := stream.Recv()
	if !errors.Is(streamErr, io.EOF) {
		t.Errorf("stream.Recv() did not return EOF in the end: %v", streamErr)
	}
}

func TestCreateChatCompletionStreamWithLogprobs(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")

		// Send test responses
		dataBytes := []byte{}

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"1","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":{"content":[],"refusal":null},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"2","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":{"content":[{"token":"Hello","logprob":-0.000020458236,"bytes":[72,101,108,108,111],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"3","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{"content":" World"},"logprobs":{"content":[{"token":" World","logprob":-0.00055303273,"bytes":[32,87,111,114,108,100],"top_logprobs":[]}],"refusal":null},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"4","object":"chat.completion.chunk","created":1729585728,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_d9767fc5b9","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		dataBytes = append(dataBytes, []byte("data: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 2000,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	expectedResponses := []openai.ChatCompletionStreamResponse{
		{
			ID:                "1",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{},
					Logprobs: &openai.ChatCompletionStreamChoiceLogprobs{
						Content: []openai.ChatCompletionTokenLogprob{},
					},
				},
			},
		},
		{
			ID:                "2",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: "Hello",
					},
					Logprobs: &openai.ChatCompletionStreamChoiceLogprobs{
						Content: []openai.ChatCompletionTokenLogprob{
							{
								Token:       "Hello",
								Logprob:     -0.000020458236,
								Bytes:       []int64{72, 101, 108, 108, 111},
								TopLogprobs: []openai.ChatCompletionTokenLogprobTopLogprob{},
							},
						},
					},
				},
			},
		},
		{
			ID:                "3",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: " World",
					},
					Logprobs: &openai.ChatCompletionStreamChoiceLogprobs{
						Content: []openai.ChatCompletionTokenLogprob{
							{
								Token:       " World",
								Logprob:     -0.00055303273,
								Bytes:       []int64{32, 87, 111, 114, 108, 100},
								TopLogprobs: []openai.ChatCompletionTokenLogprobTopLogprob{},
							},
						},
					},
				},
			},
		},
		{
			ID:                "4",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.GPT4oMini20240718,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index:        0,
					Delta:        openai.ChatCompletionStreamChoiceDelta{},
					FinishReason: "stop",
				},
			},
		},
	}

	for ix, expectedResponse := range expectedResponses {
		b, _ := json.Marshal(expectedResponse)
		t.Logf("%d: %s", ix, string(b))

		receivedResponse, streamErr := stream.Recv()
		checks.NoError(t, streamErr, "stream.Recv() failed")
		if !compareChatResponses(expectedResponse, receivedResponse) {
			t.Errorf("Stream response %v is %v, expected %v", ix, receivedResponse, expectedResponse)
		}
	}

	_, streamErr := stream.Recv()
	if !errors.Is(streamErr, io.EOF) {
		t.Errorf("stream.Recv() did not return EOF in the end: %v", streamErr)
	}
}

func TestAzureCreateChatCompletionStreamRateLimitError(t *testing.T) {
	wantCode := "429"
	wantMessage := "Requests to the Creates a completion for the chat message Operation under Azure OpenAI API " +
		"version 2023-03-15-preview have exceeded token rate limit of your current OpenAI S0 pricing tier. " +
		"Please retry after 20 seconds. " +
		"Please go here: https://aka.ms/oai/quotaincrease if you would like to further increase the default rate limit."

	client, server, teardown := setupAzureTestServer()
	defer teardown()
	server.RegisterHandler("/openai/deployments/gpt-35-turbo/chat/completions",
		func(w http.ResponseWriter, _ *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusTooManyRequests)
			// Send test responses
			dataBytes := []byte(`{"error": { "code": "` + wantCode + `", "message": "` + wantMessage + `"}}`)
			_, err := w.Write(dataBytes)

			checks.NoError(t, err, "Write error")
		})

	apiErr := &openai.APIError{}
	_, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	if !errors.As(err, &apiErr) {
		t.Errorf("Did not return APIError: %+v\n", apiErr)
		return
	}
	if apiErr.HTTPStatusCode != http.StatusTooManyRequests {
		t.Errorf("Did not return HTTPStatusCode got = %d, want = %d\n", apiErr.HTTPStatusCode, http.StatusTooManyRequests)
		return
	}
	code, ok := apiErr.Code.(string)
	if !ok || code != wantCode {
		t.Errorf("Did not return Code. got = %v, want = %s\n", apiErr.Code, wantCode)
		return
	}
	if apiErr.Message != wantMessage {
		t.Errorf("Did not return Message. got = %s, want = %s\n", apiErr.Message, wantMessage)
		return
	}
}

func TestCreateChatCompletionStreamStreamOptions(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()

	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")

		// Send test responses
		var dataBytes []byte
		//nolint:lll
		data := `{"id":"1","object":"completion","created":1598069254,"model":"gpt-3.5-turbo","system_fingerprint": "fp_d9767fc5b9","choices":[{"index":0,"delta":{"content":"response1"},"finish_reason":"max_tokens"}],"usage":null}`
		dataBytes = append(dataBytes, []byte("data: "+data+"\n\n")...)

		//nolint:lll
		data = `{"id":"2","object":"completion","created":1598069255,"model":"gpt-3.5-turbo","system_fingerprint": "fp_d9767fc5b9","choices":[{"index":0,"delta":{"content":"response2"},"finish_reason":"max_tokens"}],"usage":null}`
		dataBytes = append(dataBytes, []byte("data: "+data+"\n\n")...)

		//nolint:lll
		data = `{"id":"3","object":"completion","created":1598069256,"model":"gpt-3.5-turbo","system_fingerprint": "fp_d9767fc5b9","choices":[],"usage":{"prompt_tokens":1,"completion_tokens":1,"total_tokens":2}}`
		dataBytes = append(dataBytes, []byte("data: "+data+"\n\n")...)

		dataBytes = append(dataBytes, []byte("data: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
		StreamOptions: &openai.StreamOptions{
			IncludeUsage: true,
		},
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	expectedResponses := []openai.ChatCompletionStreamResponse{
		{
			ID:                "1",
			Object:            "completion",
			Created:           1598069254,
			Model:             openai.GPT3Dot5Turbo,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: "response1",
					},
					FinishReason: "max_tokens",
				},
			},
		},
		{
			ID:                "2",
			Object:            "completion",
			Created:           1598069255,
			Model:             openai.GPT3Dot5Turbo,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: "response2",
					},
					FinishReason: "max_tokens",
				},
			},
		},
		{
			ID:                "3",
			Object:            "completion",
			Created:           1598069256,
			Model:             openai.GPT3Dot5Turbo,
			SystemFingerprint: "fp_d9767fc5b9",
			Choices:           []openai.ChatCompletionStreamChoice{},
			Usage: &openai.Usage{
				PromptTokens:     1,
				CompletionTokens: 1,
				TotalTokens:      2,
			},
		},
	}

	for ix, expectedResponse := range expectedResponses {
		b, _ := json.Marshal(expectedResponse)
		t.Logf("%d: %s", ix, string(b))

		receivedResponse, streamErr := stream.Recv()
		checks.NoError(t, streamErr, "stream.Recv() failed")
		if !compareChatResponses(expectedResponse, receivedResponse) {
			t.Errorf("Stream response %v is %v, expected %v", ix, receivedResponse, expectedResponse)
		}
	}

	_, streamErr := stream.Recv()
	if !errors.Is(streamErr, io.EOF) {
		t.Errorf("stream.Recv() did not return EOF in the end: %v", streamErr)
	}

	_, streamErr = stream.Recv()

	checks.ErrorIs(t, streamErr, io.EOF, "stream.Recv() did not return EOF when the stream is finished")
	if !errors.Is(streamErr, io.EOF) {
		t.Errorf("stream.Recv() did not return EOF when the stream is finished: %v", streamErr)
	}
}

// Helper funcs.
func compareChatResponses(r1, r2 openai.ChatCompletionStreamResponse) bool {
	if r1.ID != r2.ID || r1.Object != r2.Object || r1.Created != r2.Created || r1.Model != r2.Model {
		return false
	}
	if len(r1.Choices) != len(r2.Choices) {
		return false
	}
	for i := range r1.Choices {
		if !compareChatStreamResponseChoices(r1.Choices[i], r2.Choices[i]) {
			return false
		}
	}
	if r1.Usage != nil || r2.Usage != nil {
		if r1.Usage == nil || r2.Usage == nil {
			return false
		}
		if r1.Usage.PromptTokens != r2.Usage.PromptTokens || r1.Usage.CompletionTokens != r2.Usage.CompletionTokens ||
			r1.Usage.TotalTokens != r2.Usage.TotalTokens {
			return false
		}
	}
	return true
}

func TestCreateChatCompletionStreamWithReasoningModel(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")

		dataBytes := []byte{}

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"1","object":"chat.completion.chunk","created":1729585728,"model":"o3-mini-2025-01-31","system_fingerprint":"fp_mini","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"2","object":"chat.completion.chunk","created":1729585728,"model":"o3-mini-2025-01-31","system_fingerprint":"fp_mini","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"3","object":"chat.completion.chunk","created":1729585728,"model":"o3-mini-2025-01-31","system_fingerprint":"fp_mini","choices":[{"index":0,"delta":{"content":" from"},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"4","object":"chat.completion.chunk","created":1729585728,"model":"o3-mini-2025-01-31","system_fingerprint":"fp_mini","choices":[{"index":0,"delta":{"content":" O3Mini"},"finish_reason":null}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		//nolint:lll
		dataBytes = append(dataBytes, []byte(`data: {"id":"5","object":"chat.completion.chunk","created":1729585728,"model":"o3-mini-2025-01-31","system_fingerprint":"fp_mini","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}`)...)
		dataBytes = append(dataBytes, []byte("\n\n")...)

		dataBytes = append(dataBytes, []byte("data: [DONE]\n\n")...)

		_, err := w.Write(dataBytes)
		checks.NoError(t, err, "Write error")
	})

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxCompletionTokens: 2000,
		Model:               openai.O3Mini20250131,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})
	checks.NoError(t, err, "CreateCompletionStream returned error")
	defer stream.Close()

	expectedResponses := []openai.ChatCompletionStreamResponse{
		{
			ID:                "1",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.O3Mini20250131,
			SystemFingerprint: "fp_mini",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Role: "assistant",
					},
				},
			},
		},
		{
			ID:                "2",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.O3Mini20250131,
			SystemFingerprint: "fp_mini",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: "Hello",
					},
				},
			},
		},
		{
			ID:                "3",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.O3Mini20250131,
			SystemFingerprint: "fp_mini",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: " from",
					},
				},
			},
		},
		{
			ID:                "4",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.O3Mini20250131,
			SystemFingerprint: "fp_mini",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index: 0,
					Delta: openai.ChatCompletionStreamChoiceDelta{
						Content: " O3Mini",
					},
				},
			},
		},
		{
			ID:                "5",
			Object:            "chat.completion.chunk",
			Created:           1729585728,
			Model:             openai.O3Mini20250131,
			SystemFingerprint: "fp_mini",
			Choices: []openai.ChatCompletionStreamChoice{
				{
					Index:        0,
					Delta:        openai.ChatCompletionStreamChoiceDelta{},
					FinishReason: "stop",
				},
			},
		},
	}

	for ix, expectedResponse := range expectedResponses {
		b, _ := json.Marshal(expectedResponse)
		t.Logf("%d: %s", ix, string(b))

		receivedResponse, streamErr := stream.Recv()
		checks.NoError(t, streamErr, "stream.Recv() failed")
		if !compareChatResponses(expectedResponse, receivedResponse) {
			t.Errorf("Stream response %v is %v, expected %v", ix, receivedResponse, expectedResponse)
		}
	}

	_, streamErr := stream.Recv()
	if !errors.Is(streamErr, io.EOF) {
		t.Errorf("stream.Recv() did not return EOF in the end: %v", streamErr)
	}
}

func TestCreateChatCompletionStreamReasoningValidatorFails(t *testing.T) {
	client, _, _ := setupOpenAITestServer()

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 100, // This will trigger the validator to fail
		Model:     openai.O3Mini,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})

	if stream != nil {
		t.Error("Expected nil stream when validation fails")
		stream.Close()
	}

	if !errors.Is(err, openai.ErrReasoningModelMaxTokensDeprecated) {
		t.Errorf("Expected ErrReasoningModelMaxTokensDeprecated, got: %v", err)
	}
}

func TestCreateChatCompletionStreamO3ReasoningValidatorFails(t *testing.T) {
	client, _, _ := setupOpenAITestServer()

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 100, // This will trigger the validator to fail
		Model:     openai.O3,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})

	if stream != nil {
		t.Error("Expected nil stream when validation fails")
		stream.Close()
	}

	if !errors.Is(err, openai.ErrReasoningModelMaxTokensDeprecated) {
		t.Errorf("Expected ErrReasoningModelMaxTokensDeprecated for O3, got: %v", err)
	}
}

func TestCreateChatCompletionStreamO4MiniReasoningValidatorFails(t *testing.T) {
	client, _, _ := setupOpenAITestServer()

	stream, err := client.CreateChatCompletionStream(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 100, // This will trigger the validator to fail
		Model:     openai.O4Mini,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
		Stream: true,
	})

	if stream != nil {
		t.Error("Expected nil stream when validation fails")
		stream.Close()
	}

	if !errors.Is(err, openai.ErrReasoningModelMaxTokensDeprecated) {
		t.Errorf("Expected ErrReasoningModelMaxTokensDeprecated for O4Mini, got: %v", err)
	}
}

func compareChatStreamResponseChoices(c1, c2 openai.ChatCompletionStreamChoice) bool {
	if c1.Index != c2.Index {
		return false
	}
	if c1.Delta.Content != c2.Delta.Content {
		return false
	}
	if c1.FinishReason != c2.FinishReason {
		return false
	}
	return true
}


================================================
FILE: chat_test.go
================================================
package openai_test

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/internal/test/checks"
	"github.com/sashabaranov/go-openai/jsonschema"
)

const (
	xCustomHeader      = "X-CUSTOM-HEADER"
	xCustomHeaderValue = "test"
)

var rateLimitHeaders = map[string]any{
	"x-ratelimit-limit-requests":     60,
	"x-ratelimit-limit-tokens":       150000,
	"x-ratelimit-remaining-requests": 59,
	"x-ratelimit-remaining-tokens":   149984,
	"x-ratelimit-reset-requests":     "1s",
	"x-ratelimit-reset-tokens":       "6m0s",
}

func TestChatCompletionsWrongModel(t *testing.T) {
	config := openai.DefaultConfig("whatever")
	config.BaseURL = "http://localhost/v1"
	client := openai.NewClientWithConfig(config)
	ctx := context.Background()

	req := openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     "ada",
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	}
	_, err := client.CreateChatCompletion(ctx, req)
	msg := fmt.Sprintf("CreateChatCompletion should return wrong model error, returned: %s", err)
	checks.ErrorIs(t, err, openai.ErrChatCompletionInvalidModel, msg)
}

func TestO1ModelsChatCompletionsDeprecatedFields(t *testing.T) {
	tests := []struct {
		name          string
		in            openai.ChatCompletionRequest
		expectedError error
	}{
		{
			name: "o1-preview_MaxTokens_deprecated",
			in: openai.ChatCompletionRequest{
				MaxTokens: 5,
				Model:     openai.O1Preview,
			},
			expectedError: openai.ErrReasoningModelMaxTokensDeprecated,
		},
		{
			name: "o1-mini_MaxTokens_deprecated",
			in: openai.ChatCompletionRequest{
				MaxTokens: 5,
				Model:     openai.O1Mini,
			},
			expectedError: openai.ErrReasoningModelMaxTokensDeprecated,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := openai.DefaultConfig("whatever")
			config.BaseURL = "http://localhost/v1"
			client := openai.NewClientWithConfig(config)
			ctx := context.Background()

			_, err := client.CreateChatCompletion(ctx, tt.in)
			checks.HasError(t, err)
			msg := fmt.Sprintf("CreateChatCompletion should return wrong model error, returned: %s", err)
			checks.ErrorIs(t, err, tt.expectedError, msg)
		})
	}
}

func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
	tests := []struct {
		name          string
		in            openai.ChatCompletionRequest
		expectedError error
	}{
		{
			name: "log_probs_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				LogProbs:            true,
				Model:               openai.O1Preview,
			},
			expectedError: openai.ErrReasoningModelLimitationsLogprobs,
		},
		{
			name: "set_temperature_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O1Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(2),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_top_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O1Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(1),
				TopP:        float32(0.1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_n_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O1Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(1),
				TopP:        float32(1),
				N:           2,
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_presence_penalty_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O1Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				PresencePenalty: float32(1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_frequency_penalty_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O1Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				FrequencyPenalty: float32(0.1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := openai.DefaultConfig("whatever")
			config.BaseURL = "http://localhost/v1"
			client := openai.NewClientWithConfig(config)
			ctx := context.Background()

			_, err := client.CreateChatCompletion(ctx, tt.in)
			checks.HasError(t, err)
			msg := fmt.Sprintf("CreateChatCompletion should return wrong model error, returned: %s", err)
			checks.ErrorIs(t, err, tt.expectedError, msg)
		})
	}
}

func TestO3ModelsChatCompletionsBetaLimitations(t *testing.T) {
	tests := []struct {
		name          string
		in            openai.ChatCompletionRequest
		expectedError error
	}{
		{
			name: "log_probs_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				LogProbs:            true,
				Model:               openai.O3Mini,
			},
			expectedError: openai.ErrReasoningModelLimitationsLogprobs,
		},
		{
			name: "set_temperature_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O3Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(2),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_top_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O3Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(1),
				TopP:        float32(0.1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_n_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O3Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(1),
				TopP:        float32(1),
				N:           2,
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_presence_penalty_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O3Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				PresencePenalty: float32(1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_frequency_penalty_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.O3Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				FrequencyPenalty: float32(0.1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := openai.DefaultConfig("whatever")
			config.BaseURL = "http://localhost/v1"
			client := openai.NewClientWithConfig(config)
			ctx := context.Background()

			_, err := client.CreateChatCompletion(ctx, tt.in)
			checks.HasError(t, err)
			msg := fmt.Sprintf("CreateChatCompletion should return wrong model error, returned: %s", err)
			checks.ErrorIs(t, err, tt.expectedError, msg)
		})
	}
}

func TestGPT5ModelsChatCompletionsBetaLimitations(t *testing.T) {
	tests := []struct {
		name          string
		in            openai.ChatCompletionRequest
		expectedError error
	}{
		{
			name: "log_probs_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				LogProbs:            true,
				Model:               openai.GPT5,
			},
			expectedError: openai.ErrReasoningModelLimitationsLogprobs,
		},
		{
			name: "set_temperature_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.GPT5Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(2),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_top_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.GPT5Nano,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(1),
				TopP:        float32(0.1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_n_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.GPT5ChatLatest,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				Temperature: float32(1),
				TopP:        float32(1),
				N:           2,
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_presence_penalty_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.GPT5,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				PresencePenalty: float32(0.1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
		{
			name: "set_frequency_penalty_unsupported",
			in: openai.ChatCompletionRequest{
				MaxCompletionTokens: 1000,
				Model:               openai.GPT5Mini,
				Messages: []openai.ChatCompletionMessage{
					{
						Role: openai.ChatMessageRoleUser,
					},
					{
						Role: openai.ChatMessageRoleAssistant,
					},
				},
				FrequencyPenalty: float32(0.1),
			},
			expectedError: openai.ErrReasoningModelLimitationsOther,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := openai.DefaultConfig("whatever")
			config.BaseURL = "http://localhost/v1"
			client := openai.NewClientWithConfig(config)
			ctx := context.Background()

			_, err := client.CreateChatCompletion(ctx, tt.in)
			checks.HasError(t, err)
			msg := fmt.Sprintf("CreateChatCompletion should return wrong model error, returned: %s", err)
			checks.ErrorIs(t, err, tt.expectedError, msg)
		})
	}
}

func TestChatRequestOmitEmpty(t *testing.T) {
	data, err := json.Marshal(openai.ChatCompletionRequest{
		// We set model b/c it's required, so omitempty doesn't make sense
		Model: "gpt-4",
	})
	checks.NoError(t, err)

	// messages is also required so isn't omitted
	const expected = `{"model":"gpt-4","messages":null}`
	if string(data) != expected {
		t.Errorf("expected JSON with all empty fields to be %v but was %v", expected, string(data))
	}
}

func TestChatCompletionsWithStream(t *testing.T) {
	config := openai.DefaultConfig("whatever")
	config.BaseURL = "http://localhost/v1"
	client := openai.NewClientWithConfig(config)
	ctx := context.Background()

	req := openai.ChatCompletionRequest{
		Stream: true,
	}
	_, err := client.CreateChatCompletion(ctx, req)
	checks.ErrorIs(t, err, openai.ErrChatCompletionStreamNotSupported, "unexpected error")
}

// TestCompletions Tests the completions endpoint of the API using the mocked server.
func TestChatCompletions(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", handleChatCompletionEndpoint)
	_, err := client.CreateChatCompletion(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	checks.NoError(t, err, "CreateChatCompletion error")
}

// TestCompletions Tests the completions endpoint of the API using the mocked server.
func TestO1ModelChatCompletions(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", handleChatCompletionEndpoint)
	_, err := client.CreateChatCompletion(context.Background(), openai.ChatCompletionRequest{
		Model:               openai.O1Preview,
		MaxCompletionTokens: 1000,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	checks.NoError(t, err, "CreateChatCompletion error")
}

func TestO3ModelChatCompletions(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", handleChatCompletionEndpoint)
	_, err := client.CreateChatCompletion(context.Background(), openai.ChatCompletionRequest{
		Model:               openai.O3Mini,
		MaxCompletionTokens: 1000,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	checks.NoError(t, err, "CreateChatCompletion error")
}

func TestDeepseekR1ModelChatCompletions(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", handleDeepseekR1ChatCompletionEndpoint)
	_, err := client.CreateChatCompletion(context.Background(), openai.ChatCompletionRequest{
		Model:               "deepseek-reasoner",
		MaxCompletionTokens: 100,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	checks.NoError(t, err, "CreateChatCompletion error")
}

// TestCompletions Tests the completions endpoint of the API using the mocked server.
func TestChatCompletionsWithHeaders(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", handleChatCompletionEndpoint)
	resp, err := client.CreateChatCompletion(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	checks.NoError(t, err, "CreateChatCompletion error")

	a := resp.Header().Get(xCustomHeader)
	_ = a
	if resp.Header().Get(xCustomHeader) != xCustomHeaderValue {
		t.Errorf("expected header %s to be %s", xCustomHeader, xCustomHeaderValue)
	}
}

// TestChatCompletionsWithRateLimitHeaders Tests the completions endpoint of the API using the mocked server.
func TestChatCompletionsWithRateLimitHeaders(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", handleChatCompletionEndpoint)
	resp, err := client.CreateChatCompletion(context.Background(), openai.ChatCompletionRequest{
		MaxTokens: 5,
		Model:     openai.GPT3Dot5Turbo,
		Messages: []openai.ChatCompletionMessage{
			{
				Role:    openai.ChatMessageRoleUser,
				Content: "Hello!",
			},
		},
	})
	checks.NoError(t, err, "CreateChatCompletion error")

	headers := resp.GetRateLimitHeaders()
	resetRequests := headers.ResetRequests.String()
	if resetRequests != rateLimitHeaders["x-ratelimit-reset-requests"] {
		t.Errorf("expected resetRequests %s to be %s", resetRequests, rateLimitHeaders["x-ratelimit-reset-requests"])
	}
	resetRequestsTime := headers.ResetRequests.Time()
	if resetRequestsTime.Before(time.Now()) {
		t.Errorf("unexpected reset requests: %v", resetRequestsTime)
	}

	bs1, _ := json.Marshal(headers)
	bs2, _ := json.Marshal(rateLimitHeaders)
	if string(bs1) != string(bs2) {
		t.Errorf("expected rate limit header %s to be %s", bs2, bs1)
	}
}

// TestChatCompletionsFunctions tests including a function call.
func TestChatCompletionsFunctions(t *testing.T) {
	client, server, teardown := setupOpenAITestServer()
	defer teardown()
	server.RegisterHandler("/v1/chat/completions", handleChatCompletionEndpoint)
	t.Run("bytes", func(t *testing.T) {
		//nolint:lll
		msg := json.RawMessage(`{"properties":{"count":{"type":"integer","description":"total number of words in sentence"},"words":{"items":{"type":"string"},"type":"array","description":"list of words in sentence"}},"type":"object","required":["count","words"]}`)
		_, err := client.CreateChatCompletion(context.Background(), openai.ChatCompletionRequest{
			MaxTokens: 5,
			Model:     openai.GPT3Dot5Turbo0613,
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "Hello!",
				},
			},
			Functions: []openai.FunctionDefinition{{
				Name:       "test",
				Parameters: &msg,
			}},
		})
		checks.NoError(t, err, "CreateChatCompletion with functions error")
	})
	t.Run("struct", func(t *testing.T) {
		type testMessage struct {
			Count int      `json:"count"`
			Words []string `json:"words"`
		}
		msg := testMessage{
			Count: 2,
			Words: []string{"hello", "world"},
		}
		_, err :=
Download .txt
gitextract_r1hjdmvj/

├── .codecov.yml
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── close-inactive-issues.yml
│       ├── integration-tests.yml
│       └── pr.yml
├── .gitignore
├── .golangci.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── api_integration_test.go
├── api_internal_test.go
├── assistant.go
├── assistant_test.go
├── audio.go
├── audio_api_test.go
├── audio_test.go
├── batch.go
├── batch_test.go
├── chat.go
├── chat_stream.go
├── chat_stream_test.go
├── chat_test.go
├── client.go
├── client_test.go
├── common.go
├── completion.go
├── completion_test.go
├── config.go
├── config_test.go
├── edits.go
├── edits_test.go
├── embeddings.go
├── embeddings_test.go
├── engines.go
├── engines_test.go
├── error.go
├── error_test.go
├── example_test.go
├── examples/
│   ├── README.md
│   ├── chatbot/
│   │   └── main.go
│   ├── completion/
│   │   └── main.go
│   ├── completion-with-tool/
│   │   └── main.go
│   ├── images/
│   │   └── main.go
│   └── voice-to-text/
│       └── main.go
├── files.go
├── files_api_test.go
├── files_test.go
├── fine_tunes.go
├── fine_tunes_test.go
├── fine_tuning_job.go
├── fine_tuning_job_test.go
├── go.mod
├── image.go
├── image_api_test.go
├── image_test.go
├── internal/
│   ├── error_accumulator.go
│   ├── error_accumulator_test.go
│   ├── form_builder.go
│   ├── form_builder_test.go
│   ├── marshaller.go
│   ├── marshaller_test.go
│   ├── request_builder.go
│   ├── request_builder_test.go
│   ├── test/
│   │   ├── checks/
│   │   │   ├── checks.go
│   │   │   └── checks_test.go
│   │   ├── failer.go
│   │   ├── failer_test.go
│   │   ├── helpers.go
│   │   ├── helpers_test.go
│   │   ├── server.go
│   │   └── server_test.go
│   ├── unmarshaler.go
│   └── unmarshaler_test.go
├── jsonschema/
│   ├── containsref_test.go
│   ├── json.go
│   ├── json_additional_test.go
│   ├── json_errors_test.go
│   ├── json_test.go
│   ├── validate.go
│   └── validate_test.go
├── messages.go
├── messages_test.go
├── models.go
├── models_test.go
├── moderation.go
├── moderation_test.go
├── openai_test.go
├── ratelimit.go
├── reasoning_validator.go
├── run.go
├── run_test.go
├── speech.go
├── speech_test.go
├── stream.go
├── stream_reader.go
├── stream_reader_test.go
├── stream_test.go
├── thread.go
├── thread_test.go
├── vector_store.go
└── vector_store_test.go
Download .txt
SYMBOL INDEX (970 symbols across 90 files)

FILE: api_integration_test.go
  function TestAPI (line 18) | func TestAPI(t *testing.T) {
  function TestCompletionStream (line 113) | func TestCompletionStream(t *testing.T) {
  function TestAPIError (line 148) | func TestAPIError(t *testing.T) {
  function TestChatCompletionResponseFormat_JSONSchema (line 183) | func TestChatCompletionResponseFormat_JSONSchema(t *testing.T) {
  function TestChatCompletionStructuredOutputsFunctionCalling (line 240) | func TestChatCompletionStructuredOutputsFunctionCalling(t *testing.T) {

FILE: api_internal_test.go
  function TestOpenAIFullURL (line 8) | func TestOpenAIFullURL(t *testing.T) {
  function TestRequestAuthHeader (line 39) | func TestRequestAuthHeader(t *testing.T) {
  function TestAzureFullURL (line 110) | func TestAzureFullURL(t *testing.T) {
  function TestCloudflareAzureFullURL (line 163) | func TestCloudflareAzureFullURL(t *testing.T) {

FILE: assistant.go
  constant assistantsSuffix (line 12) | assistantsSuffix      = "/assistants"
  constant assistantsFilesSuffix (line 13) | assistantsFilesSuffix = "/files"
  type Assistant (line 16) | type Assistant struct
  type AssistantToolType (line 35) | type AssistantToolType
  constant AssistantToolTypeCodeInterpreter (line 38) | AssistantToolTypeCodeInterpreter AssistantToolType = "code_interpreter"
  constant AssistantToolTypeRetrieval (line 39) | AssistantToolTypeRetrieval       AssistantToolType = "retrieval"
  constant AssistantToolTypeFunction (line 40) | AssistantToolTypeFunction        AssistantToolType = "function"
  constant AssistantToolTypeFileSearch (line 41) | AssistantToolTypeFileSearch      AssistantToolType = "file_search"
  type AssistantTool (line 44) | type AssistantTool struct
  type AssistantToolFileSearch (line 49) | type AssistantToolFileSearch struct
  type AssistantToolCodeInterpreter (line 53) | type AssistantToolCodeInterpreter struct
  type AssistantToolResource (line 57) | type AssistantToolResource struct
  type AssistantRequest (line 67) | type AssistantRequest struct
    method MarshalJSON (line 85) | func (a AssistantRequest) MarshalJSON() ([]byte, error) {
  type AssistantsList (line 102) | type AssistantsList struct
  type AssistantDeleteResponse (line 110) | type AssistantDeleteResponse struct
  type AssistantFile (line 118) | type AssistantFile struct
  type AssistantFileRequest (line 127) | type AssistantFileRequest struct
  type AssistantFilesList (line 131) | type AssistantFilesList struct
  method CreateAssistant (line 138) | func (c *Client) CreateAssistant(ctx context.Context, request AssistantR...
  method RetrieveAssistant (line 150) | func (c *Client) RetrieveAssistant(
  method ModifyAssistant (line 166) | func (c *Client) ModifyAssistant(
  method DeleteAssistant (line 183) | func (c *Client) DeleteAssistant(
  method ListAssistants (line 199) | func (c *Client) ListAssistants(
  method CreateAssistantFile (line 237) | func (c *Client) CreateAssistantFile(
  method RetrieveAssistantFile (line 255) | func (c *Client) RetrieveAssistantFile(
  method DeleteAssistantFile (line 272) | func (c *Client) DeleteAssistantFile(
  method ListAssistantFiles (line 289) | func (c *Client) ListAssistantFiles(

FILE: assistant_test.go
  function TestAssistant (line 16) | func TestAssistant(t *testing.T) {
  function TestAzureAssistant (line 259) | func TestAzureAssistant(t *testing.T) {

FILE: audio.go
  constant Whisper1 (line 16) | Whisper1 = "whisper-1"
  type AudioResponseFormat (line 20) | type AudioResponseFormat
  constant AudioResponseFormatJSON (line 23) | AudioResponseFormatJSON        AudioResponseFormat = "json"
  constant AudioResponseFormatText (line 24) | AudioResponseFormatText        AudioResponseFormat = "text"
  constant AudioResponseFormatSRT (line 25) | AudioResponseFormatSRT         AudioResponseFormat = "srt"
  constant AudioResponseFormatVerboseJSON (line 26) | AudioResponseFormatVerboseJSON AudioResponseFormat = "verbose_json"
  constant AudioResponseFormatVTT (line 27) | AudioResponseFormatVTT         AudioResponseFormat = "vtt"
  type TranscriptionTimestampGranularity (line 30) | type TranscriptionTimestampGranularity
  constant TranscriptionTimestampGranularityWord (line 33) | TranscriptionTimestampGranularityWord    TranscriptionTimestampGranulari...
  constant TranscriptionTimestampGranularitySegment (line 34) | TranscriptionTimestampGranularitySegment TranscriptionTimestampGranulari...
  type AudioRequest (line 38) | type AudioRequest struct
    method HasJSONResponse (line 150) | func (r AudioRequest) HasJSONResponse() bool {
  type AudioResponse (line 55) | type AudioResponse struct
  type audioTextResponse (line 82) | type audioTextResponse struct
    method ToAudioResponse (line 88) | func (r *audioTextResponse) ToAudioResponse() AudioResponse {
  method CreateTranscription (line 96) | func (c *Client) CreateTranscription(
  method CreateTranslation (line 104) | func (c *Client) CreateTranslation(
  method callAudioAPI (line 112) | func (c *Client) callAudioAPI(
  function audioMultipartForm (line 156) | func audioMultipartForm(request AudioRequest, b utils.FormBuilder) error {
  function createFileField (line 213) | func createFileField(request AudioRequest, b utils.FormBuilder) error {

FILE: audio_api_test.go
  function TestAudio (line 21) | func TestAudio(t *testing.T) {
  function TestAudioWithOptionalArgs (line 68) | func TestAudioWithOptionalArgs(t *testing.T) {
  function handleAudioEndpoint (line 114) | func handleAudioEndpoint(w http.ResponseWriter, r *http.Request) {

FILE: audio_test.go
  function TestAudioWithFailingFormBuilder (line 19) | func TestAudioWithFailingFormBuilder(t *testing.T) {
  function TestCreateFileField (line 66) | func TestCreateFileField(t *testing.T) {
  type failingFormBuilder (line 116) | type failingFormBuilder struct
    method CreateFormFile (line 118) | func (f *failingFormBuilder) CreateFormFile(_ string, _ *os.File) error {
    method CreateFormFileReader (line 122) | func (f *failingFormBuilder) CreateFormFileReader(_ string, _ io.Reade...
    method WriteField (line 126) | func (f *failingFormBuilder) WriteField(_, _ string) error {
    method Close (line 130) | func (f *failingFormBuilder) Close() error {
    method FormDataContentType (line 134) | func (f *failingFormBuilder) FormDataContentType() string {
  type failingAudioRequestBuilder (line 139) | type failingAudioRequestBuilder struct
    method Build (line 141) | func (f *failingAudioRequestBuilder) Build(
  type errorHTTPClient (line 151) | type errorHTTPClient struct
    method Do (line 153) | func (e *errorHTTPClient) Do(_ *http.Request) (*http.Response, error) {
  function TestCallAudioAPIMultipartFormError (line 157) | func TestCallAudioAPIMultipartFormError(t *testing.T) {
  function TestCallAudioAPINewRequestError (line 176) | func TestCallAudioAPINewRequestError(t *testing.T) {
  function TestCallAudioAPISendRequestErrorJSON (line 198) | func TestCallAudioAPISendRequestErrorJSON(t *testing.T) {
  function TestCallAudioAPISendRequestErrorText (line 221) | func TestCallAudioAPISendRequestErrorText(t *testing.T) {

FILE: batch.go
  constant batchesSuffix (line 12) | batchesSuffix = "/batches"
  type BatchEndpoint (line 14) | type BatchEndpoint
  constant BatchEndpointChatCompletions (line 17) | BatchEndpointChatCompletions BatchEndpoint = "/v1/chat/completions"
  constant BatchEndpointCompletions (line 18) | BatchEndpointCompletions     BatchEndpoint = "/v1/completions"
  constant BatchEndpointEmbeddings (line 19) | BatchEndpointEmbeddings      BatchEndpoint = "/v1/embeddings"
  type BatchLineItem (line 22) | type BatchLineItem interface
  type BatchChatCompletionRequest (line 26) | type BatchChatCompletionRequest struct
    method MarshalBatchLineItem (line 33) | func (r BatchChatCompletionRequest) MarshalBatchLineItem() []byte {
  type BatchCompletionRequest (line 38) | type BatchCompletionRequest struct
    method MarshalBatchLineItem (line 45) | func (r BatchCompletionRequest) MarshalBatchLineItem() []byte {
  type BatchEmbeddingRequest (line 50) | type BatchEmbeddingRequest struct
    method MarshalBatchLineItem (line 57) | func (r BatchEmbeddingRequest) MarshalBatchLineItem() []byte {
  type Batch (line 62) | type Batch struct
  type BatchRequestCounts (line 93) | type BatchRequestCounts struct
  type CreateBatchRequest (line 99) | type CreateBatchRequest struct
  type BatchResponse (line 106) | type BatchResponse struct
  method CreateBatch (line 112) | func (c *Client) CreateBatch(
  type UploadBatchFileRequest (line 129) | type UploadBatchFileRequest struct
    method MarshalJSONL (line 134) | func (r *UploadBatchFileRequest) MarshalJSONL() []byte {
    method AddChatCompletion (line 145) | func (r *UploadBatchFileRequest) AddChatCompletion(customerID string, ...
    method AddCompletion (line 154) | func (r *UploadBatchFileRequest) AddCompletion(customerID string, body...
    method AddEmbedding (line 163) | func (r *UploadBatchFileRequest) AddEmbedding(customerID string, body ...
  method UploadBatchFile (line 173) | func (c *Client) UploadBatchFile(ctx context.Context, request UploadBatc...
  type CreateBatchWithUploadFileRequest (line 184) | type CreateBatchWithUploadFileRequest struct
  method CreateBatchWithUploadFile (line 192) | func (c *Client) CreateBatchWithUploadFile(
  method RetrieveBatch (line 213) | func (c *Client) RetrieveBatch(
  method CancelBatch (line 227) | func (c *Client) CancelBatch(
  type ListBatchResponse (line 240) | type ListBatchResponse struct
  method ListBatch (line 250) | func (c *Client) ListBatch(ctx context.Context, after *string, limit *in...

FILE: batch_test.go
  function TestUploadBatchFile (line 14) | func TestUploadBatchFile(t *testing.T) {
  function TestCreateBatch (line 34) | func TestCreateBatch(t *testing.T) {
  function TestCreateBatchWithUploadFile (line 47) | func TestCreateBatchWithUploadFile(t *testing.T) {
  function TestRetrieveBatch (line 69) | func TestRetrieveBatch(t *testing.T) {
  function TestCancelBatch (line 77) | func TestCancelBatch(t *testing.T) {
  function TestListBatch (line 85) | func TestListBatch(t *testing.T) {
  function TestUploadBatchFileRequest_AddChatCompletion (line 95) | func TestUploadBatchFileRequest_AddChatCompletion(t *testing.T) {
  function TestUploadBatchFileRequest_AddCompletion (line 148) | func TestUploadBatchFileRequest_AddCompletion(t *testing.T) {
  function TestUploadBatchFileRequest_AddEmbedding (line 189) | func TestUploadBatchFileRequest_AddEmbedding(t *testing.T) {
  function handleBatchEndpoint (line 230) | func handleBatchEndpoint(w http.ResponseWriter, r *http.Request) {
  function handleRetrieveBatchEndpoint (line 302) | func handleRetrieveBatchEndpoint(w http.ResponseWriter, r *http.Request) {
  function handleCancelBatchEndpoint (line 336) | func handleCancelBatchEndpoint(w http.ResponseWriter, r *http.Request) {

FILE: chat.go
  constant ChatMessageRoleSystem (line 14) | ChatMessageRoleSystem    = "system"
  constant ChatMessageRoleUser (line 15) | ChatMessageRoleUser      = "user"
  constant ChatMessageRoleAssistant (line 16) | ChatMessageRoleAssistant = "assistant"
  constant ChatMessageRoleFunction (line 17) | ChatMessageRoleFunction  = "function"
  constant ChatMessageRoleTool (line 18) | ChatMessageRoleTool      = "tool"
  constant ChatMessageRoleDeveloper (line 19) | ChatMessageRoleDeveloper = "developer"
  constant chatCompletionsSuffix (line 22) | chatCompletionsSuffix = "/chat/completions"
  type Hate (line 30) | type Hate struct
  type SelfHarm (line 34) | type SelfHarm struct
  type Sexual (line 38) | type Sexual struct
  type Violence (line 42) | type Violence struct
  type JailBreak (line 47) | type JailBreak struct
  type Profanity (line 52) | type Profanity struct
  type ContentFilterResults (line 57) | type ContentFilterResults struct
  type PromptAnnotation (line 66) | type PromptAnnotation struct
  type ImageURLDetail (line 71) | type ImageURLDetail
  constant ImageURLDetailHigh (line 74) | ImageURLDetailHigh ImageURLDetail = "high"
  constant ImageURLDetailLow (line 75) | ImageURLDetailLow  ImageURLDetail = "low"
  constant ImageURLDetailAuto (line 76) | ImageURLDetailAuto ImageURLDetail = "auto"
  type ChatMessageImageURL (line 79) | type ChatMessageImageURL struct
  type ChatMessagePartType (line 84) | type ChatMessagePartType
  constant ChatMessagePartTypeText (line 87) | ChatMessagePartTypeText     ChatMessagePartType = "text"
  constant ChatMessagePartTypeImageURL (line 88) | ChatMessagePartTypeImageURL ChatMessagePartType = "image_url"
  type ChatMessagePart (line 91) | type ChatMessagePart struct
  type ChatCompletionMessage (line 97) | type ChatCompletionMessage struct
    method MarshalJSON (line 124) | func (m ChatCompletionMessage) MarshalJSON() ([]byte, error) {
    method UnmarshalJSON (line 157) | func (m *ChatCompletionMessage) UnmarshalJSON(bs []byte) error {
  type ToolCall (line 192) | type ToolCall struct
  type FunctionCall (line 200) | type FunctionCall struct
  type ChatCompletionResponseFormatType (line 206) | type ChatCompletionResponseFormatType
  constant ChatCompletionResponseFormatTypeJSONObject (line 209) | ChatCompletionResponseFormatTypeJSONObject ChatCompletionResponseFormatT...
  constant ChatCompletionResponseFormatTypeJSONSchema (line 210) | ChatCompletionResponseFormatTypeJSONSchema ChatCompletionResponseFormatT...
  constant ChatCompletionResponseFormatTypeText (line 211) | ChatCompletionResponseFormatTypeText       ChatCompletionResponseFormatT...
  type ChatCompletionResponseFormat (line 214) | type ChatCompletionResponseFormat struct
  type ChatCompletionResponseFormatJSONSchema (line 219) | type ChatCompletionResponseFormatJSONSchema struct
    method UnmarshalJSON (line 226) | func (r *ChatCompletionResponseFormatJSONSchema) UnmarshalJSON(data []...
  type ChatCompletionRequestExtensions (line 253) | type ChatCompletionRequestExtensions struct
  type ChatCompletionRequest (line 263) | type ChatCompletionRequest struct
  type StreamOptions (line 338) | type StreamOptions struct
  type ToolType (line 346) | type ToolType
  constant ToolTypeFunction (line 349) | ToolTypeFunction ToolType = "function"
  type Tool (line 352) | type Tool struct
  type ToolChoice (line 357) | type ToolChoice struct
  type ToolFunction (line 362) | type ToolFunction struct
  type FunctionDefinition (line 366) | type FunctionDefinition struct
  type TopLogProbs (line 381) | type TopLogProbs struct
  type LogProb (line 388) | type LogProb struct
  type LogProbs (line 398) | type LogProbs struct
  type Prediction (line 403) | type Prediction struct
  type FinishReason (line 408) | type FinishReason
    method MarshalJSON (line 428) | func (r FinishReason) MarshalJSON() ([]byte, error) {
  constant FinishReasonStop (line 411) | FinishReasonStop          FinishReason = "stop"
  constant FinishReasonLength (line 412) | FinishReasonLength        FinishReason = "length"
  constant FinishReasonFunctionCall (line 413) | FinishReasonFunctionCall  FinishReason = "function_call"
  constant FinishReasonToolCalls (line 414) | FinishReasonToolCalls     FinishReason = "tool_calls"
  constant FinishReasonContentFilter (line 415) | FinishReasonContentFilter FinishReason = "content_filter"
  constant FinishReasonNull (line 416) | FinishReasonNull          FinishReason = "null"
  type ServiceTier (line 419) | type ServiceTier
  constant ServiceTierAuto (line 422) | ServiceTierAuto     ServiceTier = "auto"
  constant ServiceTierDefault (line 423) | ServiceTierDefault  ServiceTier = "default"
  constant ServiceTierFlex (line 424) | ServiceTierFlex     ServiceTier = "flex"
  constant ServiceTierPriority (line 425) | ServiceTierPriority ServiceTier = "priority"
  type ChatCompletionChoice (line 435) | type ChatCompletionChoice struct
  type ChatCompletionResponse (line 451) | type ChatCompletionResponse struct
  method CreateChatCompletion (line 466) | func (c *Client) CreateChatCompletion(

FILE: chat_stream.go
  type ChatCompletionStreamChoiceDelta (line 8) | type ChatCompletionStreamChoiceDelta struct
  type ChatCompletionStreamChoiceLogprobs (line 22) | type ChatCompletionStreamChoiceLogprobs struct
  type ChatCompletionTokenLogprob (line 27) | type ChatCompletionTokenLogprob struct
  type ChatCompletionTokenLogprobTopLogprob (line 34) | type ChatCompletionTokenLogprobTopLogprob struct
  type ChatCompletionStreamChoice (line 40) | type ChatCompletionStreamChoice struct
  type PromptFilterResult (line 48) | type PromptFilterResult struct
  type ChatCompletionStreamResponse (line 53) | type ChatCompletionStreamResponse struct
  type ChatCompletionStream (line 70) | type ChatCompletionStream struct
  method CreateChatCompletionStream (line 78) | func (c *Client) CreateChatCompletionStream(

FILE: chat_stream_test.go
  function TestChatCompletionsStreamWrongModel (line 17) | func TestChatCompletionsStreamWrongModel(t *testing.T) {
  function TestCreateChatCompletionStream (line 39) | func TestCreateChatCompletionStream(t *testing.T) {
  function TestCreateChatCompletionStreamError (line 135) | func TestCreateChatCompletionStreamError(t *testing.T) {
  function TestCreateChatCompletionStreamWithHeaders (line 185) | func TestCreateChatCompletionStreamWithHeaders(t *testing.T) {
  function TestCreateChatCompletionStreamWithRatelimitHeaders (line 221) | func TestCreateChatCompletionStreamWithRatelimitHeaders(t *testing.T) {
  function TestCreateChatCompletionStreamErrorWithDataPrefix (line 266) | func TestCreateChatCompletionStreamErrorWithDataPrefix(t *testing.T) {
  function TestCreateChatCompletionStreamRateLimitError (line 305) | func TestCreateChatCompletionStreamRateLimitError(t *testing.T) {
  function TestCreateChatCompletionStreamWithRefusal (line 340) | func TestCreateChatCompletionStreamWithRefusal(t *testing.T) {
  function TestCreateChatCompletionStreamWithLogprobs (line 460) | func TestCreateChatCompletionStreamWithLogprobs(t *testing.T) {
  function TestAzureCreateChatCompletionStreamRateLimitError (line 605) | func TestAzureCreateChatCompletionStreamRateLimitError(t *testing.T) {
  function TestCreateChatCompletionStreamStreamOptions (line 656) | func TestCreateChatCompletionStreamStreamOptions(t *testing.T) {
  function compareChatResponses (line 771) | func compareChatResponses(r1, r2 openai.ChatCompletionStreamResponse) bo...
  function TestCreateChatCompletionStreamWithReasoningModel (line 795) | func TestCreateChatCompletionStreamWithReasoningModel(t *testing.T) {
  function TestCreateChatCompletionStreamReasoningValidatorFails (line 937) | func TestCreateChatCompletionStreamReasoningValidatorFails(t *testing.T) {
  function TestCreateChatCompletionStreamO3ReasoningValidatorFails (line 962) | func TestCreateChatCompletionStreamO3ReasoningValidatorFails(t *testing....
  function TestCreateChatCompletionStreamO4MiniReasoningValidatorFails (line 987) | func TestCreateChatCompletionStreamO4MiniReasoningValidatorFails(t *test...
  function compareChatStreamResponseChoices (line 1012) | func compareChatStreamResponseChoices(c1, c2 openai.ChatCompletionStream...

FILE: chat_test.go
  constant xCustomHeader (line 21) | xCustomHeader      = "X-CUSTOM-HEADER"
  constant xCustomHeaderValue (line 22) | xCustomHeaderValue = "test"
  function TestChatCompletionsWrongModel (line 34) | func TestChatCompletionsWrongModel(t *testing.T) {
  function TestO1ModelsChatCompletionsDeprecatedFields (line 55) | func TestO1ModelsChatCompletionsDeprecatedFields(t *testing.T) {
  function TestO1ModelsChatCompletionsBetaLimitations (line 94) | func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
  function TestO3ModelsChatCompletionsBetaLimitations (line 214) | func TestO3ModelsChatCompletionsBetaLimitations(t *testing.T) {
  function TestGPT5ModelsChatCompletionsBetaLimitations (line 334) | func TestGPT5ModelsChatCompletionsBetaLimitations(t *testing.T) {
  function TestChatRequestOmitEmpty (line 454) | func TestChatRequestOmitEmpty(t *testing.T) {
  function TestChatCompletionsWithStream (line 468) | func TestChatCompletionsWithStream(t *testing.T) {
  function TestChatCompletions (line 482) | func TestChatCompletions(t *testing.T) {
  function TestO1ModelChatCompletions (line 500) | func TestO1ModelChatCompletions(t *testing.T) {
  function TestO3ModelChatCompletions (line 517) | func TestO3ModelChatCompletions(t *testing.T) {
  function TestDeepseekR1ModelChatCompletions (line 534) | func TestDeepseekR1ModelChatCompletions(t *testing.T) {
  function TestChatCompletionsWithHeaders (line 552) | func TestChatCompletionsWithHeaders(t *testing.T) {
  function TestChatCompletionsWithRateLimitHeaders (line 576) | func TestChatCompletionsWithRateLimitHeaders(t *testing.T) {
  function TestChatCompletionsFunctions (line 610) | func TestChatCompletionsFunctions(t *testing.T) {
  function TestAzureChatCompletions (line 759) | func TestAzureChatCompletions(t *testing.T) {
  function TestMultipartChatCompletions (line 777) | func TestMultipartChatCompletions(t *testing.T) {
  function TestMultipartChatMessageSerialization (line 807) | func TestMultipartChatMessageSerialization(t *testing.T) {
  function handleChatCompletionEndpoint (line 880) | func handleChatCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
  function handleDeepseekR1ChatCompletionEndpoint (line 962) | func handleDeepseekR1ChatCompletionEndpoint(w http.ResponseWriter, r *ht...
  function getChatCompletionBody (line 1025) | func getChatCompletionBody(r *http.Request) (openai.ChatCompletionReques...
  function TestFinishReason (line 1039) | func TestFinishReason(t *testing.T) {
  function TestChatCompletionResponseFormatJSONSchema_UnmarshalJSON (line 1070) | func TestChatCompletionResponseFormatJSONSchema_UnmarshalJSON(t *testing...
  function TestChatCompletionRequest_UnmarshalJSON (line 1150) | func TestChatCompletionRequest_UnmarshalJSON(t *testing.T) {

FILE: client.go
  type Client (line 17) | type Client struct
    method newRequest (line 113) | func (c *Client) newRequest(ctx context.Context, method, url string, s...
    method sendRequest (line 130) | func (c *Client) sendRequest(req *http.Request, v Response) error {
    method sendRequestRaw (line 158) | func (c *Client) sendRequestRaw(req *http.Request) (response RawRespon...
    method setCommonHeaders (line 197) | func (c *Client) setCommonHeaders(req *http.Request) {
    method fullURL (line 270) | func (c *Client) fullURL(suffix string, setters ...fullURLOption) stri...
    method suffixWithAPIVersion (line 287) | func (c *Client) suffixWithAPIVersion(suffix string) string {
    method baseURLWithAzureDeployment (line 297) | func (c *Client) baseURLWithAzureDeployment(baseURL, suffix, model str...
    method handleErrorResp (line 309) | func (c *Client) handleErrorResp(resp *http.Response) error {
  type Response (line 24) | type Response interface
  type httpHeader (line 28) | type httpHeader
    method SetHeader (line 30) | func (h *httpHeader) SetHeader(header http.Header) {
    method Header (line 34) | func (h *httpHeader) Header() http.Header {
    method GetRateLimitHeaders (line 38) | func (h *httpHeader) GetRateLimitHeaders() RateLimitHeaders {
  type RawResponse (line 42) | type RawResponse struct
  function NewClient (line 49) | func NewClient(authToken string) *Client {
  function NewClientWithConfig (line 55) | func NewClientWithConfig(config ClientConfig) *Client {
  function NewOrgClient (line 68) | func NewOrgClient(authToken, org string) *Client {
  type requestOptions (line 74) | type requestOptions struct
  type requestOption (line 79) | type requestOption
  function withBody (line 81) | func withBody(body any) requestOption {
  function withExtraBody (line 87) | func withExtraBody(extraBody map[string]any) requestOption {
  function withContentType (line 101) | func withContentType(contentType string) requestOption {
  function withBetaAssistantVersion (line 107) | func withBetaAssistantVersion(version string) requestOption {
  function sendRequestStream (line 174) | func sendRequestStream[T streamable](client *Client, req *http.Request) ...
  function isFailureStatusCode (line 219) | func isFailureStatusCode(resp *http.Response) bool {
  function decodeResponse (line 223) | func decodeResponse(body io.Reader, v any) error {
  function decodeString (line 238) | func decodeString(body io.Reader, output *string) error {
  type fullURLOptions (line 247) | type fullURLOptions struct
  type fullURLOption (line 251) | type fullURLOption
  function withModel (line 253) | func withModel(model string) fullURLOption {
  function containsSubstr (line 334) | func containsSubstr(s []string, e string) bool {

FILE: client_test.go
  type failingRequestBuilder (line 19) | type failingRequestBuilder struct
    method Build (line 21) | func (*failingRequestBuilder) Build(_ context.Context, _, _ string, _ ...
  function TestClient (line 25) | func TestClient(t *testing.T) {
  function TestSetCommonHeadersAnthropic (line 42) | func TestSetCommonHeadersAnthropic(t *testing.T) {
  function TestDecodeResponse (line 57) | func TestDecodeResponse(t *testing.T) {
  type errorReader (line 129) | type errorReader struct
    method Read (line 133) | func (e *errorReader) Read(_ []byte) (n int, err error) {
  function TestHandleErrorResp (line 137) | func TestHandleErrorResp(t *testing.T) {
  function TestClientReturnsRequestBuilderErrors (line 266) | func TestClientReturnsRequestBuilderErrors(t *testing.T) {
  function TestClientReturnsRequestBuilderErrorsAddition (line 473) | func TestClientReturnsRequestBuilderErrorsAddition(t *testing.T) {
  function TestClient_suffixWithAPIVersion (line 488) | func TestClient_suffixWithAPIVersion(t *testing.T) {
  function TestClient_baseURLWithAzureDeployment (line 549) | func TestClient_baseURLWithAzureDeployment(t *testing.T) {

FILE: common.go
  type Usage (line 6) | type Usage struct
  type CompletionTokensDetails (line 15) | type CompletionTokensDetails struct
  type PromptTokensDetails (line 23) | type PromptTokensDetails struct

FILE: completion.go
  constant O1Mini (line 13) | O1Mini                  = "o1-mini"
  constant O1Mini20240912 (line 14) | O1Mini20240912          = "o1-mini-2024-09-12"
  constant O1Preview (line 15) | O1Preview               = "o1-preview"
  constant O1Preview20240912 (line 16) | O1Preview20240912       = "o1-preview-2024-09-12"
  constant O1 (line 17) | O1                      = "o1"
  constant O120241217 (line 18) | O120241217              = "o1-2024-12-17"
  constant O3 (line 19) | O3                      = "o3"
  constant O320250416 (line 20) | O320250416              = "o3-2025-04-16"
  constant O3Mini (line 21) | O3Mini                  = "o3-mini"
  constant O3Mini20250131 (line 22) | O3Mini20250131          = "o3-mini-2025-01-31"
  constant O4Mini (line 23) | O4Mini                  = "o4-mini"
  constant O4Mini20250416 (line 24) | O4Mini20250416          = "o4-mini-2025-04-16"
  constant GPT432K0613 (line 25) | GPT432K0613             = "gpt-4-32k-0613"
  constant GPT432K0314 (line 26) | GPT432K0314             = "gpt-4-32k-0314"
  constant GPT432K (line 27) | GPT432K                 = "gpt-4-32k"
  constant GPT40613 (line 28) | GPT40613                = "gpt-4-0613"
  constant GPT40314 (line 29) | GPT40314                = "gpt-4-0314"
  constant GPT4o (line 30) | GPT4o                   = "gpt-4o"
  constant GPT4o20240513 (line 31) | GPT4o20240513           = "gpt-4o-2024-05-13"
  constant GPT4o20240806 (line 32) | GPT4o20240806           = "gpt-4o-2024-08-06"
  constant GPT4o20241120 (line 33) | GPT4o20241120           = "gpt-4o-2024-11-20"
  constant GPT4oLatest (line 34) | GPT4oLatest             = "chatgpt-4o-latest"
  constant GPT4oMini (line 35) | GPT4oMini               = "gpt-4o-mini"
  constant GPT4oMini20240718 (line 36) | GPT4oMini20240718       = "gpt-4o-mini-2024-07-18"
  constant GPT4Turbo (line 37) | GPT4Turbo               = "gpt-4-turbo"
  constant GPT4Turbo20240409 (line 38) | GPT4Turbo20240409       = "gpt-4-turbo-2024-04-09"
  constant GPT4Turbo0125 (line 39) | GPT4Turbo0125           = "gpt-4-0125-preview"
  constant GPT4Turbo1106 (line 40) | GPT4Turbo1106           = "gpt-4-1106-preview"
  constant GPT4TurboPreview (line 41) | GPT4TurboPreview        = "gpt-4-turbo-preview"
  constant GPT4VisionPreview (line 42) | GPT4VisionPreview       = "gpt-4-vision-preview"
  constant GPT4 (line 43) | GPT4                    = "gpt-4"
  constant GPT4Dot1 (line 44) | GPT4Dot1                = "gpt-4.1"
  constant GPT4Dot120250414 (line 45) | GPT4Dot120250414        = "gpt-4.1-2025-04-14"
  constant GPT4Dot1Mini (line 46) | GPT4Dot1Mini            = "gpt-4.1-mini"
  constant GPT4Dot1Mini20250414 (line 47) | GPT4Dot1Mini20250414    = "gpt-4.1-mini-2025-04-14"
  constant GPT4Dot1Nano (line 48) | GPT4Dot1Nano            = "gpt-4.1-nano"
  constant GPT4Dot1Nano20250414 (line 49) | GPT4Dot1Nano20250414    = "gpt-4.1-nano-2025-04-14"
  constant GPT4Dot5Preview (line 50) | GPT4Dot5Preview         = "gpt-4.5-preview"
  constant GPT4Dot5Preview20250227 (line 51) | GPT4Dot5Preview20250227 = "gpt-4.5-preview-2025-02-27"
  constant GPT5 (line 52) | GPT5                    = "gpt-5"
  constant GPT5Mini (line 53) | GPT5Mini                = "gpt-5-mini"
  constant GPT5Nano (line 54) | GPT5Nano                = "gpt-5-nano"
  constant GPT5ChatLatest (line 55) | GPT5ChatLatest          = "gpt-5-chat-latest"
  constant GPT3Dot5Turbo0125 (line 56) | GPT3Dot5Turbo0125       = "gpt-3.5-turbo-0125"
  constant GPT3Dot5Turbo1106 (line 57) | GPT3Dot5Turbo1106       = "gpt-3.5-turbo-1106"
  constant GPT3Dot5Turbo0613 (line 58) | GPT3Dot5Turbo0613       = "gpt-3.5-turbo-0613"
  constant GPT3Dot5Turbo0301 (line 59) | GPT3Dot5Turbo0301       = "gpt-3.5-turbo-0301"
  constant GPT3Dot5Turbo16K (line 60) | GPT3Dot5Turbo16K        = "gpt-3.5-turbo-16k"
  constant GPT3Dot5Turbo16K0613 (line 61) | GPT3Dot5Turbo16K0613    = "gpt-3.5-turbo-16k-0613"
  constant GPT3Dot5Turbo (line 62) | GPT3Dot5Turbo           = "gpt-3.5-turbo"
  constant GPT3Dot5TurboInstruct (line 63) | GPT3Dot5TurboInstruct   = "gpt-3.5-turbo-instruct"
  constant GPT3TextDavinci003 (line 65) | GPT3TextDavinci003 = "text-davinci-003"
  constant GPT3TextDavinci002 (line 67) | GPT3TextDavinci002 = "text-davinci-002"
  constant GPT3TextCurie001 (line 69) | GPT3TextCurie001 = "text-curie-001"
  constant GPT3TextBabbage001 (line 71) | GPT3TextBabbage001 = "text-babbage-001"
  constant GPT3TextAda001 (line 73) | GPT3TextAda001 = "text-ada-001"
  constant GPT3TextDavinci001 (line 75) | GPT3TextDavinci001 = "text-davinci-001"
  constant GPT3DavinciInstructBeta (line 77) | GPT3DavinciInstructBeta = "davinci-instruct-beta"
  constant GPT3Davinci (line 79) | GPT3Davinci    = "davinci"
  constant GPT3Davinci002 (line 80) | GPT3Davinci002 = "davinci-002"
  constant GPT3CurieInstructBeta (line 82) | GPT3CurieInstructBeta = "curie-instruct-beta"
  constant GPT3Curie (line 83) | GPT3Curie             = "curie"
  constant GPT3Curie002 (line 84) | GPT3Curie002          = "curie-002"
  constant GPT3Ada (line 86) | GPT3Ada    = "ada"
  constant GPT3Ada002 (line 87) | GPT3Ada002 = "ada-002"
  constant GPT3Babbage (line 89) | GPT3Babbage    = "babbage"
  constant GPT3Babbage002 (line 90) | GPT3Babbage002 = "babbage-002"
  constant CodexCodeDavinci002 (line 97) | CodexCodeDavinci002 = "code-davinci-002"
  constant CodexCodeCushman001 (line 98) | CodexCodeCushman001 = "code-cushman-001"
  constant CodexCodeDavinci001 (line 99) | CodexCodeDavinci001 = "code-davinci-001"
  function checkEndpointSupportsModel (line 173) | func checkEndpointSupportsModel(endpoint, model string) bool {
  function checkPromptType (line 177) | func checkPromptType(prompt any) bool {
  type CompletionRequest (line 200) | type CompletionRequest struct
  type CompletionChoice (line 231) | type CompletionChoice struct
  type LogprobResult (line 239) | type LogprobResult struct
  type CompletionResponse (line 247) | type CompletionResponse struct
  method CreateCompletion (line 263) | func (c *Client) CreateCompletion(

FILE: completion_test.go
  function TestCompletionsWrongModel (line 19) | func TestCompletionsWrongModel(t *testing.T) {
  function TestCompletionsWrongModelO3 (line 37) | func TestCompletionsWrongModelO3(t *testing.T) {
  function TestCompletionsWrongModelO4Mini (line 55) | func TestCompletionsWrongModelO4Mini(t *testing.T) {
  function TestCompletionWithStream (line 72) | func TestCompletionWithStream(t *testing.T) {
  function TestCompletions (line 85) | func TestCompletions(t *testing.T) {
  function TestMultiplePromptsCompletionsWrong (line 100) | func TestMultiplePromptsCompletionsWrong(t *testing.T) {
  function TestMultiplePromptsCompletions (line 117) | func TestMultiplePromptsCompletions(t *testing.T) {
  function handleCompletionEndpoint (line 131) | func handleCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
  function getCompletionBody (line 207) | func getCompletionBody(r *http.Request) (openai.CompletionRequest, error) {
  function TestCompletionWithO1Model (line 222) | func TestCompletionWithO1Model(t *testing.T) {
  function TestCompletionWithGPT4DotModels (line 240) | func TestCompletionWithGPT4DotModels(t *testing.T) {
  function TestCompletionWithGPT4oModels (line 273) | func TestCompletionWithGPT4oModels(t *testing.T) {
  function TestCompletionWithGPT5Models (line 305) | func TestCompletionWithGPT5Models(t *testing.T) {

FILE: config.go
  constant openaiAPIURLv1 (line 10) | openaiAPIURLv1                 = "https://api.openai.com/v1"
  constant defaultEmptyMessagesLimit (line 11) | defaultEmptyMessagesLimit uint = 300
  constant azureAPIPrefix (line 13) | azureAPIPrefix         = "openai"
  constant azureDeploymentsPrefix (line 14) | azureDeploymentsPrefix = "deployments"
  constant AnthropicAPIVersion (line 16) | AnthropicAPIVersion = "2023-06-01"
  type APIType (line 19) | type APIType
  constant APITypeOpenAI (line 22) | APITypeOpenAI          APIType = "OPEN_AI"
  constant APITypeAzure (line 23) | APITypeAzure           APIType = "AZURE"
  constant APITypeAzureAD (line 24) | APITypeAzureAD         APIType = "AZURE_AD"
  constant APITypeCloudflareAzure (line 25) | APITypeCloudflareAzure APIType = "CLOUDFLARE_AZURE"
  constant APITypeAnthropic (line 26) | APITypeAnthropic       APIType = "ANTHROPIC"
  constant AzureAPIKeyHeader (line 29) | AzureAPIKeyHeader = "api-key"
  constant defaultAssistantVersion (line 31) | defaultAssistantVersion = "v2"
  type HTTPDoer (line 33) | type HTTPDoer interface
  type ClientConfig (line 38) | type ClientConfig struct
    method String (line 104) | func (ClientConfig) String() string {
    method GetAzureDeploymentByModel (line 108) | func (c ClientConfig) GetAzureDeploymentByModel(model string) string {
  function DefaultConfig (line 52) | func DefaultConfig(authToken string) ClientConfig {
  function DefaultAzureConfig (line 66) | func DefaultAzureConfig(apiKey, baseURL string) ClientConfig {
  function DefaultAnthropicConfig (line 87) | func DefaultAnthropicConfig(apiKey, baseURL string) ClientConfig {

FILE: config_test.go
  function TestGetAzureDeploymentByModel (line 9) | func TestGetAzureDeploymentByModel(t *testing.T) {
  function TestDefaultAnthropicConfig (line 68) | func TestDefaultAnthropicConfig(t *testing.T) {
  function TestDefaultAnthropicConfigWithEmptyValues (line 91) | func TestDefaultAnthropicConfigWithEmptyValues(t *testing.T) {
  function TestClientConfigString (line 108) | func TestClientConfigString(t *testing.T) {
  function TestGetAzureDeploymentByModel_NoMapper (line 118) | func TestGetAzureDeploymentByModel_NoMapper(t *testing.T) {

FILE: edits.go
  type EditsRequest (line 10) | type EditsRequest struct
  type EditsChoice (line 20) | type EditsChoice struct
  type EditsResponse (line 26) | type EditsResponse struct
  method Edits (line 40) | func (c *Client) Edits(ctx context.Context, request EditsRequest) (respo...

FILE: edits_test.go
  function TestEdits (line 17) | func TestEdits(t *testing.T) {
  function handleEditEndpoint (line 40) | func handleEditEndpoint(w http.ResponseWriter, r *http.Request) {
  function getEditBody (line 80) | func getEditBody(r *http.Request) (openai.EditsRequest, error) {

FILE: embeddings.go
  type EmbeddingModel (line 17) | type EmbeddingModel
  constant AdaSimilarity (line 21) | AdaSimilarity         EmbeddingModel = "text-similarity-ada-001"
  constant BabbageSimilarity (line 22) | BabbageSimilarity     EmbeddingModel = "text-similarity-babbage-001"
  constant CurieSimilarity (line 23) | CurieSimilarity       EmbeddingModel = "text-similarity-curie-001"
  constant DavinciSimilarity (line 24) | DavinciSimilarity     EmbeddingModel = "text-similarity-davinci-001"
  constant AdaSearchDocument (line 25) | AdaSearchDocument     EmbeddingModel = "text-search-ada-doc-001"
  constant AdaSearchQuery (line 26) | AdaSearchQuery        EmbeddingModel = "text-search-ada-query-001"
  constant BabbageSearchDocument (line 27) | BabbageSearchDocument EmbeddingModel = "text-search-babbage-doc-001"
  constant BabbageSearchQuery (line 28) | BabbageSearchQuery    EmbeddingModel = "text-search-babbage-query-001"
  constant CurieSearchDocument (line 29) | CurieSearchDocument   EmbeddingModel = "text-search-curie-doc-001"
  constant CurieSearchQuery (line 30) | CurieSearchQuery      EmbeddingModel = "text-search-curie-query-001"
  constant DavinciSearchDocument (line 31) | DavinciSearchDocument EmbeddingModel = "text-search-davinci-doc-001"
  constant DavinciSearchQuery (line 32) | DavinciSearchQuery    EmbeddingModel = "text-search-davinci-query-001"
  constant AdaCodeSearchCode (line 33) | AdaCodeSearchCode     EmbeddingModel = "code-search-ada-code-001"
  constant AdaCodeSearchText (line 34) | AdaCodeSearchText     EmbeddingModel = "code-search-ada-text-001"
  constant BabbageCodeSearchCode (line 35) | BabbageCodeSearchCode EmbeddingModel = "code-search-babbage-code-001"
  constant BabbageCodeSearchText (line 36) | BabbageCodeSearchText EmbeddingModel = "code-search-babbage-text-001"
  constant AdaEmbeddingV2 (line 38) | AdaEmbeddingV2  EmbeddingModel = "text-embedding-ada-002"
  constant SmallEmbedding3 (line 39) | SmallEmbedding3 EmbeddingModel = "text-embedding-3-small"
  constant LargeEmbedding3 (line 40) | LargeEmbedding3 EmbeddingModel = "text-embedding-3-large"
  type Embedding (line 49) | type Embedding struct
    method DotProduct (line 59) | func (e *Embedding) DotProduct(other *Embedding) (float32, error) {
  type EmbeddingResponse (line 73) | type EmbeddingResponse struct
  type base64String (line 82) | type base64String
    method Decode (line 84) | func (b base64String) Decode() ([]float32, error) {
  type Base64Embedding (line 100) | type Base64Embedding struct
  type EmbeddingResponseBase64 (line 107) | type EmbeddingResponseBase64 struct
    method ToEmbeddingResponse (line 117) | func (r *EmbeddingResponseBase64) ToEmbeddingResponse() (EmbeddingResp...
  type EmbeddingRequestConverter (line 141) | type EmbeddingRequestConverter interface
  type EmbeddingEncodingFormat (line 149) | type EmbeddingEncodingFormat
  constant EmbeddingEncodingFormatFloat (line 152) | EmbeddingEncodingFormatFloat  EmbeddingEncodingFormat = "float"
  constant EmbeddingEncodingFormatBase64 (line 153) | EmbeddingEncodingFormatBase64 EmbeddingEncodingFormat = "base64"
  type EmbeddingRequest (line 156) | type EmbeddingRequest struct
    method Convert (line 169) | func (r EmbeddingRequest) Convert() EmbeddingRequest {
  type EmbeddingRequestStrings (line 174) | type EmbeddingRequestStrings struct
    method Convert (line 199) | func (r EmbeddingRequestStrings) Convert() EmbeddingRequest {
  type EmbeddingRequestTokens (line 210) | type EmbeddingRequestTokens struct
    method Convert (line 235) | func (r EmbeddingRequestTokens) Convert() EmbeddingRequest {
  method CreateEmbeddings (line 251) | func (c *Client) CreateEmbeddings(

FILE: embeddings_test.go
  function TestEmbedding (line 18) | func TestEmbedding(t *testing.T) {
  function TestEmbeddingEndpoint (line 102) | func TestEmbeddingEndpoint(t *testing.T) {
  function TestAzureEmbeddingEndpoint (line 205) | func TestAzureEmbeddingEndpoint(t *testing.T) {
  function TestEmbeddingResponseBase64_ToEmbeddingResponse (line 231) | func TestEmbeddingResponseBase64_ToEmbeddingResponse(t *testing.T) {
  function TestDotProduct (line 293) | func TestDotProduct(t *testing.T) {

FILE: engines.go
  type Engine (line 10) | type Engine struct
  type EnginesList (line 20) | type EnginesList struct
  method ListEngines (line 28) | func (c *Client) ListEngines(ctx context.Context) (engines EnginesList, ...
  method GetEngine (line 40) | func (c *Client) GetEngine(

FILE: engines_test.go
  function TestGetEngine (line 15) | func TestGetEngine(t *testing.T) {
  function TestListEngines (line 27) | func TestListEngines(t *testing.T) {
  function TestListEnginesReturnError (line 38) | func TestListEnginesReturnError(t *testing.T) {

FILE: error.go
  type APIError (line 11) | type APIError struct
    method Error (line 39) | func (e *APIError) Error() string {
    method UnmarshalJSON (line 47) | func (e *APIError) UnmarshalJSON(data []byte) (err error) {
  type InnerError (line 22) | type InnerError struct
  type RequestError (line 28) | type RequestError struct
    method Error (line 106) | func (e *RequestError) Error() string {
    method Unwrap (line 113) | func (e *RequestError) Unwrap() error {
  type ErrorResponse (line 35) | type ErrorResponse struct

FILE: error_test.go
  function TestAPIErrorUnmarshalJSON (line 12) | func TestAPIErrorUnmarshalJSON(t *testing.T) {
  function assertAPIErrorMessage (line 221) | func assertAPIErrorMessage(t *testing.T, apiErr openai.APIError, expecte...
  function assertAPIErrorInnerError (line 227) | func assertAPIErrorInnerError(t *testing.T, apiErr openai.APIError, expe...
  function assertAPIErrorCode (line 233) | func assertAPIErrorCode(t *testing.T, apiErr openai.APIError, expected i...
  function assertAPIErrorParam (line 249) | func assertAPIErrorParam(t *testing.T, apiErr openai.APIError, expected ...
  function assertAPIErrorType (line 255) | func assertAPIErrorType(t *testing.T, apiErr openai.APIError, typ string) {
  function TestRequestError (line 261) | func TestRequestError(t *testing.T) {

FILE: example_test.go
  function Example (line 17) | func Example() {
  function ExampleClient_CreateChatCompletionStream (line 39) | func ExampleClient_CreateChatCompletionStream() {
  function ExampleClient_CreateCompletion (line 80) | func ExampleClient_CreateCompletion() {
  function ExampleClient_CreateCompletionStream (line 97) | func ExampleClient_CreateCompletionStream() {
  function ExampleClient_CreateTranscription (line 131) | func ExampleClient_CreateTranscription() {
  function ExampleClient_CreateTranscription_captions (line 147) | func ExampleClient_CreateTranscription_captions() {
  function ExampleClient_CreateTranslation (line 174) | func ExampleClient_CreateTranslation() {
  function ExampleClient_CreateImage (line 190) | func ExampleClient_CreateImage() {
  function ExampleClient_CreateImage_base64 (line 209) | func ExampleClient_CreateImage_base64() {
  function ExampleClientConfig_clientWithProxy (line 248) | func ExampleClientConfig_clientWithProxy() {
  function Example_chatbot (line 272) | func Example_chatbot() {
  function ExampleDefaultAzureConfig (line 304) | func ExampleDefaultAzureConfig() {
  function ExampleAPIError (line 332) | func ExampleAPIError() {

FILE: examples/chatbot/main.go
  function main (line 12) | func main() {

FILE: examples/completion-with-tool/main.go
  function main (line 12) | func main() {

FILE: examples/completion/main.go
  function main (line 11) | func main() {

FILE: examples/images/main.go
  function main (line 11) | func main() {

FILE: examples/voice-to-text/main.go
  function main (line 12) | func main() {

FILE: files.go
  type FileRequest (line 11) | type FileRequest struct
  type PurposeType (line 18) | type PurposeType
  constant PurposeFineTune (line 21) | PurposeFineTune         PurposeType = "fine-tune"
  constant PurposeFineTuneResults (line 22) | PurposeFineTuneResults  PurposeType = "fine-tune-results"
  constant PurposeAssistants (line 23) | PurposeAssistants       PurposeType = "assistants"
  constant PurposeAssistantsOutput (line 24) | PurposeAssistantsOutput PurposeType = "assistants_output"
  constant PurposeBatch (line 25) | PurposeBatch            PurposeType = "batch"
  type FileBytesRequest (line 29) | type FileBytesRequest struct
  type File (line 39) | type File struct
  type FilesList (line 53) | type FilesList struct
  method CreateFileBytes (line 60) | func (c *Client) CreateFileBytes(ctx context.Context, request FileBytesR...
  method CreateFile (line 92) | func (c *Client) CreateFile(ctx context.Context, request FileRequest) (f...
  method DeleteFile (line 128) | func (c *Client) DeleteFile(ctx context.Context, fileID string) (err err...
  method ListFiles (line 140) | func (c *Client) ListFiles(ctx context.Context) (files FilesList, err er...
  method GetFile (line 152) | func (c *Client) GetFile(ctx context.Context, fileID string) (file File,...
  method GetFileContent (line 163) | func (c *Client) GetFileContent(ctx context.Context, fileID string) (con...

FILE: files_api_test.go
  function TestFileBytesUpload (line 19) | func TestFileBytesUpload(t *testing.T) {
  function TestFileUpload (line 32) | func TestFileUpload(t *testing.T) {
  function handleCreateFile (line 46) | func handleCreateFile(w http.ResponseWriter, r *http.Request) {
  function TestDeleteFile (line 86) | func TestDeleteFile(t *testing.T) {
  function TestListFile (line 94) | func TestListFile(t *testing.T) {
  function TestGetFile (line 105) | func TestGetFile(t *testing.T) {
  function TestGetFileContent (line 116) | func TestGetFileContent(t *testing.T) {
  function TestGetFileContentReturnError (line 141) | func TestGetFileContentReturnError(t *testing.T) {
  function TestGetFileContentReturnTimeoutError (line 179) | func TestGetFileContentReturnTimeoutError(t *testing.T) {

FILE: files_test.go
  function TestFileBytesUploadWithFailingFormBuilder (line 14) | func TestFileBytesUploadWithFailingFormBuilder(t *testing.T) {
  function TestFileUploadWithFailingFormBuilder (line 61) | func TestFileUploadWithFailingFormBuilder(t *testing.T) {
  function TestFileUploadWithNonExistentPath (line 111) | func TestFileUploadWithNonExistentPath(t *testing.T) {
  function TestCreateFileRequestBuilderFailure (line 124) | func TestCreateFileRequestBuilderFailure(t *testing.T) {

FILE: fine_tunes.go
  type FineTuneRequest (line 12) | type FineTuneRequest struct
  type FineTune (line 30) | type FineTune struct
  type FineTuneEvent (line 51) | type FineTuneEvent struct
  type FineTuneHyperParams (line 61) | type FineTuneHyperParams struct
  type FineTuneList (line 71) | type FineTuneList struct
  type FineTuneEventList (line 81) | type FineTuneEventList struct
  type FineTuneDeleteResponse (line 91) | type FineTuneDeleteResponse struct
  method CreateFineTune (line 102) | func (c *Client) CreateFineTune(ctx context.Context, request FineTuneReq...
  method CancelFineTune (line 117) | func (c *Client) CancelFineTune(ctx context.Context, fineTuneID string) ...
  method ListFineTunes (line 130) | func (c *Client) ListFineTunes(ctx context.Context) (response FineTuneLi...
  method GetFineTune (line 143) | func (c *Client) GetFineTune(ctx context.Context, fineTuneID string) (re...
  method DeleteFineTune (line 157) | func (c *Client) DeleteFineTune(ctx context.Context, fineTuneID string) ...
  method ListFineTuneEvents (line 170) | func (c *Client) ListFineTuneEvents(ctx context.Context, fineTuneID stri...

FILE: fine_tunes_test.go
  constant testFineTuneID (line 14) | testFineTuneID = "fine-tune-id"
  function TestFineTunes (line 17) | func TestFineTunes(t *testing.T) {

FILE: fine_tuning_job.go
  type FineTuningJob (line 10) | type FineTuningJob struct
  type Hyperparameters (line 28) | type Hyperparameters struct
  type FineTuningJobRequest (line 34) | type FineTuningJobRequest struct
  type FineTuningJobEventList (line 42) | type FineTuningJobEventList struct
  type FineTuningJobEvent (line 50) | type FineTuningJobEvent struct
  method CreateFineTuningJob (line 61) | func (c *Client) CreateFineTuningJob(
  method CancelFineTuningJob (line 76) | func (c *Client) CancelFineTuningJob(ctx context.Context, fineTuningJobI...
  method RetrieveFineTuningJob (line 87) | func (c *Client) RetrieveFineTuningJob(
  type listFineTuningJobEventsParameters (line 101) | type listFineTuningJobEventsParameters struct
  type ListFineTuningJobEventsParameter (line 106) | type ListFineTuningJobEventsParameter
  function ListFineTuningJobEventsWithAfter (line 108) | func ListFineTuningJobEventsWithAfter(after string) ListFineTuningJobEve...
  function ListFineTuningJobEventsWithLimit (line 114) | func ListFineTuningJobEventsWithLimit(limit int) ListFineTuningJobEvents...
  method ListFineTuningJobEvents (line 121) | func (c *Client) ListFineTuningJobEvents(

FILE: fine_tuning_job_test.go
  constant testFineTuninigJobID (line 14) | testFineTuninigJobID = "fine-tuning-job-id"
  function TestFineTuningJob (line 17) | func TestFineTuningJob(t *testing.T) {

FILE: image.go
  constant CreateImageSize256x256 (line 13) | CreateImageSize256x256   = "256x256"
  constant CreateImageSize512x512 (line 14) | CreateImageSize512x512   = "512x512"
  constant CreateImageSize1024x1024 (line 15) | CreateImageSize1024x1024 = "1024x1024"
  constant CreateImageSize1792x1024 (line 18) | CreateImageSize1792x1024 = "1792x1024"
  constant CreateImageSize1024x1792 (line 19) | CreateImageSize1024x1792 = "1024x1792"
  constant CreateImageSize1536x1024 (line 22) | CreateImageSize1536x1024 = "1536x1024"
  constant CreateImageSize1024x1536 (line 23) | CreateImageSize1024x1536 = "1024x1536"
  constant CreateImageResponseFormatB64JSON (line 28) | CreateImageResponseFormatB64JSON = "b64_json"
  constant CreateImageResponseFormatURL (line 29) | CreateImageResponseFormatURL     = "url"
  constant CreateImageModelDallE2 (line 33) | CreateImageModelDallE2    = "dall-e-2"
  constant CreateImageModelDallE3 (line 34) | CreateImageModelDallE3    = "dall-e-3"
  constant CreateImageModelGptImage1 (line 35) | CreateImageModelGptImage1 = "gpt-image-1"
  constant CreateImageQualityHD (line 39) | CreateImageQualityHD       = "hd"
  constant CreateImageQualityStandard (line 40) | CreateImageQualityStandard = "standard"
  constant CreateImageQualityHigh (line 43) | CreateImageQualityHigh   = "high"
  constant CreateImageQualityMedium (line 44) | CreateImageQualityMedium = "medium"
  constant CreateImageQualityLow (line 45) | CreateImageQualityLow    = "low"
  constant CreateImageStyleVivid (line 50) | CreateImageStyleVivid   = "vivid"
  constant CreateImageStyleNatural (line 51) | CreateImageStyleNatural = "natural"
  constant CreateImageBackgroundTransparent (line 56) | CreateImageBackgroundTransparent = "transparent"
  constant CreateImageBackgroundOpaque (line 57) | CreateImageBackgroundOpaque      = "opaque"
  constant CreateImageModerationLow (line 62) | CreateImageModerationLow = "low"
  constant CreateImageOutputFormatPNG (line 67) | CreateImageOutputFormatPNG  = "png"
  constant CreateImageOutputFormatJPEG (line 68) | CreateImageOutputFormatJPEG = "jpeg"
  constant CreateImageOutputFormatWEBP (line 69) | CreateImageOutputFormatWEBP = "webp"
  type ImageRequest (line 73) | type ImageRequest struct
  type ImageResponse (line 89) | type ImageResponse struct
  type ImageResponseInputTokensDetails (line 98) | type ImageResponseInputTokensDetails struct
  type ImageResponseUsage (line 104) | type ImageResponseUsage struct
  type ImageResponseDataInner (line 112) | type ImageResponseDataInner struct
  method CreateImage (line 119) | func (c *Client) CreateImage(ctx context.Context, request ImageRequest) ...
  function WrapReader (line 136) | func WrapReader(rdr io.Reader, filename string, contentType string) io.R...
  type file (line 140) | type file struct
    method Name (line 146) | func (f file) Name() string {
    method ContentType (line 155) | func (f file) ContentType() string {
  type ImageEditRequest (line 161) | type ImageEditRequest struct
  method CreateEditImage (line 174) | func (c *Client) CreateEditImage(ctx context.Context, request ImageEditR...
  type ImageVariRequest (line 235) | type ImageVariRequest struct
  method CreateVariImage (line 246) | func (c *Client) CreateVariImage(ctx context.Context, request ImageVariR...

FILE: image_api_test.go
  function TestImages (line 18) | func TestImages(t *testing.T) {
  function handleImageEndpoint (line 36) | func handleImageEndpoint(w http.ResponseWriter, r *http.Request) {
  function getImageBody (line 71) | func getImageBody(r *http.Request) (openai.ImageRequest, error) {
  function TestImageEdit (line 85) | func TestImageEdit(t *testing.T) {
  function TestImageEditWithoutMask (line 113) | func TestImageEditWithoutMask(t *testing.T) {
  function handleEditImageEndpoint (line 135) | func handleEditImageEndpoint(w http.ResponseWriter, r *http.Request) {
  function TestImageVariation (line 165) | func TestImageVariation(t *testing.T) {
  function handleVariateImageEndpoint (line 186) | func handleVariateImageEndpoint(w http.ResponseWriter, r *http.Request) {

FILE: image_test.go
  type mockFormBuilder (line 15) | type mockFormBuilder struct
    method CreateFormFile (line 22) | func (fb *mockFormBuilder) CreateFormFile(fieldname string, file *os.F...
    method CreateFormFileReader (line 26) | func (fb *mockFormBuilder) CreateFormFileReader(fieldname string, r io...
    method WriteField (line 30) | func (fb *mockFormBuilder) WriteField(fieldname, value string) error {
    method Close (line 34) | func (fb *mockFormBuilder) Close() error {
    method FormDataContentType (line 38) | func (fb *mockFormBuilder) FormDataContentType() string {
  function TestImageFormBuilderFailures (line 42) | func TestImageFormBuilderFailures(t *testing.T) {
  function TestVariImageFormBuilderFailures (line 173) | func TestVariImageFormBuilderFailures(t *testing.T) {
  type testNamedReader (line 276) | type testNamedReader struct
    method Name (line 278) | func (testNamedReader) Name() string { return "named.txt" }
  function TestWrapReader (line 280) | func TestWrapReader(t *testing.T) {

FILE: internal/error_accumulator.go
  type ErrorAccumulator (line 9) | type ErrorAccumulator interface
  type errorBuffer (line 14) | type errorBuffer interface
  type DefaultErrorAccumulator (line 20) | type DefaultErrorAccumulator struct
    method Write (line 30) | func (e *DefaultErrorAccumulator) Write(p []byte) error {
    method Bytes (line 38) | func (e *DefaultErrorAccumulator) Bytes() (errBytes []byte) {
  function NewErrorAccumulator (line 24) | func NewErrorAccumulator() ErrorAccumulator {

FILE: internal/error_accumulator_test.go
  function TestDefaultErrorAccumulator_WriteMultiple (line 11) | func TestDefaultErrorAccumulator_WriteMultiple(t *testing.T) {
  function TestDefaultErrorAccumulator_EmptyBuffer (line 25) | func TestDefaultErrorAccumulator_EmptyBuffer(t *testing.T) {
  function TestDefaultErrorAccumulator_WriteError (line 35) | func TestDefaultErrorAccumulator_WriteError(t *testing.T) {

FILE: internal/form_builder.go
  type FormBuilder (line 13) | type FormBuilder interface
  type DefaultFormBuilder (line 21) | type DefaultFormBuilder struct
    method CreateFormFile (line 31) | func (fb *DefaultFormBuilder) CreateFormFile(fieldname string, file *o...
    method CreateFormFileReader (line 43) | func (fb *DefaultFormBuilder) CreateFormFileReader(fieldname string, r...
    method createFormFile (line 81) | func (fb *DefaultFormBuilder) createFormFile(fieldname string, r io.Re...
    method WriteField (line 99) | func (fb *DefaultFormBuilder) WriteField(fieldname, value string) error {
    method Close (line 106) | func (fb *DefaultFormBuilder) Close() error {
    method FormDataContentType (line 110) | func (fb *DefaultFormBuilder) FormDataContentType() string {
  function NewFormBuilder (line 25) | func NewFormBuilder(body io.Writer) *DefaultFormBuilder {
  function escapeQuotes (line 37) | func escapeQuotes(s string) string {

FILE: internal/form_builder_test.go
  type mockFormBuilder (line 15) | type mockFormBuilder struct
    method CreateFormFile (line 21) | func (m *mockFormBuilder) CreateFormFile(fieldname string, file *os.Fi...
    method WriteField (line 25) | func (m *mockFormBuilder) WriteField(fieldname, value string) error {
    method Close (line 29) | func (m *mockFormBuilder) Close() error {
    method FormDataContentType (line 33) | func (m *mockFormBuilder) FormDataContentType() string {
  function TestCloseMethod (line 37) | func TestCloseMethod(t *testing.T) {
  type failingWriter (line 56) | type failingWriter struct
    method Write (line 61) | func (*failingWriter) Write([]byte) (int, error) {
  function TestFormBuilderWithFailingWriter (line 65) | func TestFormBuilderWithFailingWriter(t *testing.T) {
  function TestFormBuilderWithClosedFile (line 77) | func TestFormBuilderWithClosedFile(t *testing.T) {
  type failingReader (line 91) | type failingReader struct
    method Read (line 96) | func (*failingReader) Read([]byte) (int, error) {
  type readerWithNameAndContentType (line 100) | type readerWithNameAndContentType struct
    method Name (line 104) | func (*readerWithNameAndContentType) Name() string {
    method ContentType (line 108) | func (*readerWithNameAndContentType) ContentType() string {
  function TestFormBuilderWithReader (line 112) | func TestFormBuilderWithReader(t *testing.T) {
  function TestFormDataContentType (line 136) | func TestFormDataContentType(t *testing.T) {
  function TestWriteField (line 148) | func TestWriteField(t *testing.T) {
  function TestCreateFormFile (line 166) | func TestCreateFormFile(t *testing.T) {
  function TestCreateFormFileSuccess (line 180) | func TestCreateFormFileSuccess(t *testing.T) {

FILE: internal/marshaller.go
  type Marshaller (line 7) | type Marshaller interface
  type JSONMarshaller (line 11) | type JSONMarshaller struct
    method Marshal (line 13) | func (jm *JSONMarshaller) Marshal(value any) ([]byte, error) {

FILE: internal/marshaller_test.go
  function TestJSONMarshaller_Normal (line 10) | func TestJSONMarshaller_Normal(t *testing.T) {
  function TestJSONMarshaller_InvalidInput (line 21) | func TestJSONMarshaller_InvalidInput(t *testing.T) {
  function TestJSONMarshaller_EmptyValue (line 27) | func TestJSONMarshaller_EmptyValue(t *testing.T) {

FILE: internal/request_builder.go
  type RequestBuilder (line 10) | type RequestBuilder interface
  type HTTPRequestBuilder (line 14) | type HTTPRequestBuilder struct
    method Build (line 24) | func (b *HTTPRequestBuilder) Build(
  function NewRequestBuilder (line 18) | func NewRequestBuilder() *HTTPRequestBuilder {

FILE: internal/request_builder_test.go
  type failingMarshaller (line 15) | type failingMarshaller struct
    method Marshal (line 17) | func (*failingMarshaller) Marshal(_ any) ([]byte, error) {
  function TestRequestBuilderReturnsMarshallerErrors (line 21) | func TestRequestBuilderReturnsMarshallerErrors(t *testing.T) {
  function TestRequestBuilderReturnsRequest (line 32) | func TestRequestBuilderReturnsRequest(t *testing.T) {
  function TestRequestBuilderReturnsRequestWhenRequestOfArgsIsNil (line 50) | func TestRequestBuilderReturnsRequestWhenRequestOfArgsIsNil(t *testing.T) {
  function TestRequestBuilderWithReaderBodyAndHeader (line 64) | func TestRequestBuilderWithReaderBodyAndHeader(t *testing.T) {
  function TestRequestBuilderInvalidURL (line 90) | func TestRequestBuilderInvalidURL(t *testing.T) {

FILE: internal/test/checks/checks.go
  function NoError (line 8) | func NoError(t *testing.T, err error, message ...string) {
  function NoErrorF (line 15) | func NoErrorF(t *testing.T, err error, message ...string) {
  function HasError (line 22) | func HasError(t *testing.T, err error, message ...string) {
  function ErrorIs (line 29) | func ErrorIs(t *testing.T, err, target error, msg ...string) {
  function ErrorIsF (line 36) | func ErrorIsF(t *testing.T, err, target error, format string, msg ...str...
  function ErrorIsNot (line 43) | func ErrorIsNot(t *testing.T, err, target error, msg ...string) {
  function ErrorIsNotf (line 50) | func ErrorIsNotf(t *testing.T, err, target error, format string, msg ......

FILE: internal/test/checks/checks_test.go
  function TestChecksSuccessPaths (line 10) | func TestChecksSuccessPaths(t *testing.T) {

FILE: internal/test/failer.go
  type FailingErrorBuffer (line 9) | type FailingErrorBuffer struct
    method Write (line 11) | func (b *FailingErrorBuffer) Write(_ []byte) (n int, err error) {
    method Len (line 15) | func (b *FailingErrorBuffer) Len() int {
    method Bytes (line 19) | func (b *FailingErrorBuffer) Bytes() []byte {

FILE: internal/test/failer_test.go
  function TestFailingErrorBuffer (line 9) | func TestFailingErrorBuffer(t *testing.T) {

FILE: internal/test/helpers.go
  function CreateTestFile (line 12) | func CreateTestFile(t *testing.T, path string) {
  type TokenRoundTripper (line 27) | type TokenRoundTripper struct
    method RoundTrip (line 40) | func (t *TokenRoundTripper) RoundTrip(req *http.Request) (*http.Respon...

FILE: internal/test/helpers_test.go
  function TestCreateTestFile (line 14) | func TestCreateTestFile(t *testing.T) {
  function TestTokenRoundTripperAddsHeader (line 27) | func TestTokenRoundTripperAddsHeader(t *testing.T) {

FILE: internal/test/server.go
  constant testAPI (line 11) | testAPI = "this-is-my-secure-token-do-not-steal!!"
  function GetTestToken (line 13) | func GetTestToken() string {
  type ServerTest (line 17) | type ServerTest struct
    method HandlerCount (line 27) | func (ts *ServerTest) HandlerCount() int {
    method HasHandler (line 32) | func (ts *ServerTest) HasHandler(path string) bool {
    method RegisterHandler (line 38) | func (ts *ServerTest) RegisterHandler(path string, handler handler) {
    method OpenAITestServer (line 46) | func (ts *ServerTest) OpenAITestServer() *httptest.Server {
  type handler (line 20) | type handler
  function NewTestServer (line 22) | func NewTestServer() *ServerTest {

FILE: internal/test/server_test.go
  function TestGetTestToken (line 11) | func TestGetTestToken(t *testing.T) {
  function TestNewTestServer (line 17) | func TestNewTestServer(t *testing.T) {
  function TestRegisterHandlerTransformsPath (line 27) | func TestRegisterHandlerTransformsPath(t *testing.T) {
  function TestOpenAITestServer (line 36) | func TestOpenAITestServer(t *testing.T) {

FILE: internal/unmarshaler.go
  type Unmarshaler (line 7) | type Unmarshaler interface
  type JSONUnmarshaler (line 11) | type JSONUnmarshaler struct
    method Unmarshal (line 13) | func (jm *JSONUnmarshaler) Unmarshal(data []byte, v any) error {

FILE: internal/unmarshaler_test.go
  function TestJSONUnmarshaler_Normal (line 10) | func TestJSONUnmarshaler_Normal(t *testing.T) {
  function TestJSONUnmarshaler_InvalidJSON (line 22) | func TestJSONUnmarshaler_InvalidJSON(t *testing.T) {
  function TestJSONUnmarshaler_EmptyInput (line 31) | func TestJSONUnmarshaler_EmptyInput(t *testing.T) {

FILE: jsonschema/containsref_test.go
  type SelfRef (line 10) | type SelfRef struct
  type Address (line 15) | type Address struct
  type Person (line 19) | type Person struct
  function TestGenerateSchemaForType_SelfRef (line 25) | func TestGenerateSchemaForType_SelfRef(t *testing.T) {
  function TestGenerateSchemaForType_NoSelfRef (line 37) | func TestGenerateSchemaForType_NoSelfRef(t *testing.T) {

FILE: jsonschema/json.go
  type DataType (line 15) | type DataType
  constant Object (line 18) | Object  DataType = "object"
  constant Number (line 19) | Number  DataType = "number"
  constant Integer (line 20) | Integer DataType = "integer"
  constant String (line 21) | String  DataType = "string"
  constant Array (line 22) | Array   DataType = "array"
  constant Null (line 23) | Null    DataType = "null"
  constant Boolean (line 24) | Boolean DataType = "boolean"
  type Definition (line 29) | type Definition struct
    method MarshalJSON (line 58) | func (d *Definition) MarshalJSON() ([]byte, error) {
    method Unmarshal (line 70) | func (d *Definition) Unmarshal(content string, v any) error {
  function GenerateSchemaForType (line 74) | func GenerateSchemaForType(v any) (*Definition, error) {
  function reflectSchema (line 105) | func reflectSchema(t reflect.Type, defs map[string]Definition) (*Definit...
  function reflectSchemaObject (line 158) | func reflectSchemaObject(t reflect.Type, defs map[string]Definition) (*D...
  function containsRef (line 214) | func containsRef(def Definition, targetRef string) bool {

FILE: jsonschema/json_additional_test.go
  function TestDefinitionUnmarshal (line 11) | func TestDefinitionUnmarshal(t *testing.T) {
  function TestGenerateSchemaForTypeUnsupported (line 45) | func TestGenerateSchemaForTypeUnsupported(t *testing.T) {
  function TestValidateInvalidContainers (line 55) | func TestValidateInvalidContainers(t *testing.T) {
  function TestValidateRefNotFound (line 68) | func TestValidateRefNotFound(t *testing.T) {

FILE: jsonschema/json_errors_test.go
  function TestGenerateSchemaForType_ErrorPaths (line 10) | func TestGenerateSchemaForType_ErrorPaths(t *testing.T) {

FILE: jsonschema/json_test.go
  function TestDefinition_MarshalJSON (line 11) | func TestDefinition_MarshalJSON(t *testing.T) {
  type User (line 185) | type User struct
  type Order (line 191) | type Order struct
  function TestStructToSchema (line 197) | func TestStructToSchema(t *testing.T) {
  function structToMap (line 655) | func structToMap(t *testing.T, v any) map[string]any {

FILE: jsonschema/validate.go
  function CollectDefs (line 8) | func CollectDefs(def Definition) map[string]Definition {
  function collectDefsRecursive (line 14) | func collectDefsRecursive(def Definition, result map[string]Definition, ...
  function VerifySchemaAndUnmarshal (line 28) | func VerifySchemaAndUnmarshal(schema Definition, content []byte, v any) ...
  type validateArgs (line 40) | type validateArgs struct
  type ValidateOption (line 44) | type ValidateOption
  function WithDefs (line 46) | func WithDefs(defs map[string]Definition) ValidateOption {
  function Validate (line 52) | func Validate(schema Definition, data any, opts ...ValidateOption) bool {
  function validateObject (line 99) | func validateObject(schema Definition, data any, defs map[string]Definit...
  function validateArray (line 120) | func validateArray(schema Definition, data any, defs map[string]Definiti...
  function contains (line 133) | func contains[S ~[]E, E comparable](s S, v E) bool {

FILE: jsonschema/validate_test.go
  function Test_Validate (line 10) | func Test_Validate(t *testing.T) {
  function TestUnmarshal (line 174) | func TestUnmarshal(t *testing.T) {
  function TestCollectDefs (line 255) | func TestCollectDefs(t *testing.T) {

FILE: messages.go
  constant messagesSuffix (line 11) | messagesSuffix = "messages"
  type Message (line 14) | type Message struct
  type MessagesList (line 29) | type MessagesList struct
  type MessageContent (line 40) | type MessageContent struct
  type MessageText (line 46) | type MessageText struct
  type ImageFile (line 51) | type ImageFile struct
  type ImageURL (line 55) | type ImageURL struct
  type MessageRequest (line 60) | type MessageRequest struct
  type MessageFile (line 68) | type MessageFile struct
  type MessageFilesList (line 77) | type MessageFilesList struct
  type MessageDeletionStatus (line 83) | type MessageDeletionStatus struct
  method CreateMessage (line 92) | func (c *Client) CreateMessage(ctx context.Context, threadID string, req...
  method ListMessage (line 105) | func (c *Client) ListMessage(ctx context.Context, threadID string,
  method RetrieveMessage (line 146) | func (c *Client) RetrieveMessage(
  method ModifyMessage (line 162) | func (c *Client) ModifyMessage(
  method RetrieveMessageFile (line 179) | func (c *Client) RetrieveMessageFile(
  method ListMessageFiles (line 195) | func (c *Client) ListMessageFiles(
  method DeleteMessage (line 211) | func (c *Client) DeleteMessage(

FILE: messages_test.go
  function setupServerForTestMessage (line 17) | func setupServerForTestMessage(t *testing.T, server *test.ServerTest) {
  function TestMessages (line 186) | func TestMessages(t *testing.T) {

FILE: models.go
  type Model (line 10) | type Model struct
  type Permission (line 23) | type Permission struct
  type FineTuneModelDeleteResponse (line 39) | type FineTuneModelDeleteResponse struct
  type ModelsList (line 48) | type ModelsList struct
  method ListModels (line 56) | func (c *Client) ListModels(ctx context.Context) (models ModelsList, err...
  method GetModel (line 68) | func (c *Client) GetModel(ctx context.Context, modelID string) (model Mo...
  method DeleteFineTuneModel (line 81) | func (c *Client) DeleteFineTuneModel(ctx context.Context, modelID string) (

FILE: models_test.go
  constant testFineTuneModelID (line 16) | testFineTuneModelID = "fine-tune-model-id"
  function TestListModels (line 19) | func TestListModels(t *testing.T) {
  function TestAzureListModels (line 27) | func TestAzureListModels(t *testing.T) {
  function handleListModelsEndpoint (line 36) | func handleListModelsEndpoint(w http.ResponseWriter, _ *http.Request) {
  function TestGetModel (line 42) | func TestGetModel(t *testing.T) {
  function TestGetModelO3 (line 51) | func TestGetModelO3(t *testing.T) {
  function TestGetModelO4Mini (line 60) | func TestGetModelO4Mini(t *testing.T) {
  function TestAzureGetModel (line 68) | func TestAzureGetModel(t *testing.T) {
  function handleGetModelEndpoint (line 77) | func handleGetModelEndpoint(w http.ResponseWriter, _ *http.Request) {
  function TestGetModelReturnTimeoutError (line 82) | func TestGetModelReturnTimeoutError(t *testing.T) {
  function TestDeleteFineTuneModel (line 101) | func TestDeleteFineTuneModel(t *testing.T) {
  function handleDeleteFineTuneModelEndpoint (line 109) | func handleDeleteFineTuneModelEndpoint(w http.ResponseWriter, _ *http.Re...

FILE: moderation.go
  constant ModerationOmniLatest (line 17) | ModerationOmniLatest   = "omni-moderation-latest"
  constant ModerationOmni20240926 (line 18) | ModerationOmni20240926 = "omni-moderation-2024-09-26"
  constant ModerationTextStable (line 19) | ModerationTextStable   = "text-moderation-stable"
  constant ModerationTextLatest (line 20) | ModerationTextLatest   = "text-moderation-latest"
  constant ModerationText001 (line 22) | ModerationText001 = "text-moderation-001"
  type ModerationRequest (line 37) | type ModerationRequest struct
  type Result (line 43) | type Result struct
  type ResultCategories (line 50) | type ResultCategories struct
  type ResultCategoryScores (line 65) | type ResultCategoryScores struct
  type ModerationResponse (line 80) | type ModerationResponse struct
  method Moderations (line 90) | func (c *Client) Moderations(ctx context.Context, request ModerationRequ...

FILE: moderation_test.go
  function TestModerations (line 19) | func TestModerations(t *testing.T) {
  function TestModerationsWithDifferentModelOptions (line 31) | func TestModerationsWithDifferentModelOptions(t *testing.T) {
  function getModerationModelTestOption (line 57) | func getModerationModelTestOption(model string, expect error) struct {
  function handleModerationEndpoint (line 68) | func handleModerationEndpoint(w http.ResponseWriter, r *http.Request) {
  function getModerationBody (line 143) | func getModerationBody(r *http.Request) (openai.ModerationRequest, error) {

FILE: openai_test.go
  function setupOpenAITestServer (line 8) | func setupOpenAITestServer() (client *openai.Client, server *test.Server...
  function setupAzureTestServer (line 19) | func setupAzureTestServer() (client *openai.Client, server *test.ServerT...
  function numTokens (line 35) | func numTokens(s string) int {

FILE: ratelimit.go
  type RateLimitHeaders (line 10) | type RateLimitHeaders struct
  type ResetTime (line 19) | type ResetTime
    method String (line 21) | func (r ResetTime) String() string {
    method Time (line 25) | func (r ResetTime) Time() time.Time {
  function newRateLimitHeaders (line 30) | func newRateLimitHeaders(h http.Header) RateLimitHeaders {

FILE: reasoning_validator.go
  type ReasoningValidator (line 32) | type ReasoningValidator struct
    method Validate (line 40) | func (v *ReasoningValidator) Validate(request ChatCompletionRequest) e...
    method validateReasoningModelParams (line 58) | func (v *ReasoningValidator) validateReasoningModelParams(request Chat...
  function NewReasoningValidator (line 35) | func NewReasoningValidator() *ReasoningValidator {

FILE: run.go
  type Run (line 10) | type Run struct
  type RunStatus (line 44) | type RunStatus
  constant RunStatusQueued (line 47) | RunStatusQueued         RunStatus = "queued"
  constant RunStatusInProgress (line 48) | RunStatusInProgress     RunStatus = "in_progress"
  constant RunStatusRequiresAction (line 49) | RunStatusRequiresAction RunStatus = "requires_action"
  constant RunStatusCancelling (line 50) | RunStatusCancelling     RunStatus = "cancelling"
  constant RunStatusFailed (line 51) | RunStatusFailed         RunStatus = "failed"
  constant RunStatusCompleted (line 52) | RunStatusCompleted      RunStatus = "completed"
  constant RunStatusIncomplete (line 53) | RunStatusIncomplete     RunStatus = "incomplete"
  constant RunStatusExpired (line 54) | RunStatusExpired        RunStatus = "expired"
  constant RunStatusCancelled (line 55) | RunStatusCancelled      RunStatus = "cancelled"
  type RunRequiredAction (line 58) | type RunRequiredAction struct
  type RequiredActionType (line 63) | type RequiredActionType
  constant RequiredActionTypeSubmitToolOutputs (line 66) | RequiredActionTypeSubmitToolOutputs RequiredActionType = "submit_tool_ou...
  type SubmitToolOutputs (line 69) | type SubmitToolOutputs struct
  type RunLastError (line 73) | type RunLastError struct
  type RunError (line 78) | type RunError
  constant RunErrorServerError (line 81) | RunErrorServerError       RunError = "server_error"
  constant RunErrorRateLimitExceeded (line 82) | RunErrorRateLimitExceeded RunError = "rate_limit_exceeded"
  type RunRequest (line 85) | type RunRequest struct
  type ThreadTruncationStrategy (line 120) | type ThreadTruncationStrategy struct
  type TruncationStrategy (line 128) | type TruncationStrategy
  constant TruncationStrategyAuto (line 132) | TruncationStrategyAuto = TruncationStrategy("auto")
  constant TruncationStrategyLastMessages (line 134) | TruncationStrategyLastMessages = TruncationStrategy("last_messages")
  type ReponseFormat (line 140) | type ReponseFormat struct
  type RunModifyRequest (line 144) | type RunModifyRequest struct
  type RunList (line 149) | type RunList struct
  type SubmitToolOutputsRequest (line 155) | type SubmitToolOutputsRequest struct
  type ToolOutput (line 159) | type ToolOutput struct
  type CreateThreadAndRunRequest (line 164) | type CreateThreadAndRunRequest struct
  type RunStep (line 169) | type RunStep struct
  type RunStepStatus (line 189) | type RunStepStatus
  constant RunStepStatusInProgress (line 192) | RunStepStatusInProgress RunStepStatus = "in_progress"
  constant RunStepStatusCancelling (line 193) | RunStepStatusCancelling RunStepStatus = "cancelled"
  constant RunStepStatusFailed (line 194) | RunStepStatusFailed     RunStepStatus = "failed"
  constant RunStepStatusCompleted (line 195) | RunStepStatusCompleted  RunStepStatus = "completed"
  constant RunStepStatusExpired (line 196) | RunStepStatusExpired    RunStepStatus = "expired"
  type RunStepType (line 199) | type RunStepType
  constant RunStepTypeMessageCreation (line 202) | RunStepTypeMessageCreation RunStepType = "message_creation"
  constant RunStepTypeToolCalls (line 203) | RunStepTypeToolCalls       RunStepType = "tool_calls"
  type StepDetails (line 206) | type StepDetails struct
  type StepDetailsMessageCreation (line 212) | type StepDetailsMessageCreation struct
  type RunStepList (line 217) | type RunStepList struct
  type Pagination (line 227) | type Pagination struct
  method CreateRun (line 235) | func (c *Client) CreateRun(
  method RetrieveRun (line 256) | func (c *Client) RetrieveRun(
  method ModifyRun (line 276) | func (c *Client) ModifyRun(
  method ListRuns (line 298) | func (c *Client) ListRuns(
  method SubmitToolOutputs (line 337) | func (c *Client) SubmitToolOutputs(
  method CancelRun (line 358) | func (c *Client) CancelRun(
  method CreateThreadAndRun (line 377) | func (c *Client) CreateThreadAndRun(
  method RetrieveRunStep (line 396) | func (c *Client) RetrieveRunStep(
  method ListRunSteps (line 417) | func (c *Client) ListRunSteps(

FILE: run_test.go
  function TestRun (line 16) | func TestRun(t *testing.T) {

FILE: speech.go
  type SpeechModel (line 8) | type SpeechModel
  constant TTSModel1 (line 11) | TTSModel1         SpeechModel = "tts-1"
  constant TTSModel1HD (line 12) | TTSModel1HD       SpeechModel = "tts-1-hd"
  constant TTSModelCanary (line 13) | TTSModelCanary    SpeechModel = "canary-tts"
  constant TTSModelGPT4oMini (line 14) | TTSModelGPT4oMini SpeechModel = "gpt-4o-mini-tts"
  type SpeechVoice (line 17) | type SpeechVoice
  constant VoiceAlloy (line 20) | VoiceAlloy   SpeechVoice = "alloy"
  constant VoiceAsh (line 21) | VoiceAsh     SpeechVoice = "ash"
  constant VoiceBallad (line 22) | VoiceBallad  SpeechVoice = "ballad"
  constant VoiceCoral (line 23) | VoiceCoral   SpeechVoice = "coral"
  constant VoiceEcho (line 24) | VoiceEcho    SpeechVoice = "echo"
  constant VoiceFable (line 25) | VoiceFable   SpeechVoice = "fable"
  constant VoiceOnyx (line 26) | VoiceOnyx    SpeechVoice = "onyx"
  constant VoiceNova (line 27) | VoiceNova    SpeechVoice = "nova"
  constant VoiceShimmer (line 28) | VoiceShimmer SpeechVoice = "shimmer"
  constant VoiceVerse (line 29) | VoiceVerse   SpeechVoice = "verse"
  type SpeechResponseFormat (line 32) | type SpeechResponseFormat
  constant SpeechResponseFormatMp3 (line 35) | SpeechResponseFormatMp3  SpeechResponseFormat = "mp3"
  constant SpeechResponseFormatOpus (line 36) | SpeechResponseFormatOpus SpeechResponseFormat = "opus"
  constant SpeechResponseFormatAac (line 37) | SpeechResponseFormatAac  SpeechResponseFormat = "aac"
  constant SpeechResponseFormatFlac (line 38) | SpeechResponseFormatFlac SpeechResponseFormat = "flac"
  constant SpeechResponseFormatWav (line 39) | SpeechResponseFormatWav  SpeechResponseFormat = "wav"
  constant SpeechResponseFormatPcm (line 40) | SpeechResponseFormatPcm  SpeechResponseFormat = "pcm"
  type CreateSpeechRequest (line 43) | type CreateSpeechRequest struct
  method CreateSpeech (line 52) | func (c *Client) CreateSpeech(ctx context.Context, request CreateSpeechR...

FILE: speech_test.go
  function TestSpeechIntegration (line 19) | func TestSpeechIntegration(t *testing.T) {

FILE: stream.go
  type CompletionStream (line 13) | type CompletionStream struct
  method CreateCompletionStream (line 21) | func (c *Client) CreateCompletionStream(

FILE: stream_reader.go
  type streamable (line 19) | type streamable interface
  type streamReader (line 23) | type streamReader struct
  method Recv (line 35) | func (stream *streamReader[T]) Recv() (response T, err error) {
  method RecvRaw (line 48) | func (stream *streamReader[T]) RecvRaw() ([]byte, error) {
  method processLines (line 57) | func (stream *streamReader[T]) processLines() ([]byte, error) {
  method unmarshalError (line 103) | func (stream *streamReader[T]) unmarshalError() (errResp *ErrorResponse) {
  method Close (line 117) | func (stream *streamReader[T]) Close() error {

FILE: stream_reader_test.go
  type failingUnMarshaller (line 16) | type failingUnMarshaller struct
    method Unmarshal (line 18) | func (*failingUnMarshaller) Unmarshal(_ []byte, _ any) error {
  function TestStreamReaderReturnsUnmarshalerErrors (line 22) | func TestStreamReaderReturnsUnmarshalerErrors(t *testing.T) {
  function TestStreamReaderReturnsErrTooManyEmptyStreamMessages (line 44) | func TestStreamReaderReturnsErrTooManyEmptyStreamMessages(t *testing.T) {
  function TestStreamReaderReturnsErrTestErrorAccumulatorWriteFailed (line 55) | func TestStreamReaderReturnsErrTestErrorAccumulatorWriteFailed(t *testin...
  function TestStreamReaderRecvRaw (line 67) | func TestStreamReaderRecvRaw(t *testing.T) {

FILE: stream_test.go
  function TestCompletionsStreamWrongModel (line 17) | func TestCompletionsStreamWrongModel(t *testing.T) {
  function TestCreateCompletionStream (line 34) | func TestCreateCompletionStream(t *testing.T) {
  function TestCreateCompletionStreamError (line 106) | func TestCreateCompletionStreamError(t *testing.T) {
  function TestCreateCompletionStreamRateLimitError (line 151) | func TestCreateCompletionStreamRateLimitError(t *testing.T) {
  function TestCreateCompletionStreamTooManyEmptyStreamMessagesError (line 182) | func TestCreateCompletionStreamTooManyEmptyStreamMessagesError(t *testin...
  function TestCreateCompletionStreamUnexpectedTerminatedError (line 228) | func TestCreateCompletionStreamUnexpectedTerminatedError(t *testing.T) {
  function TestCreateCompletionStreamBrokenJSONError (line 263) | func TestCreateCompletionStreamBrokenJSONError(t *testing.T) {
  function TestCreateCompletionStreamReturnTimeoutError (line 305) | func TestCreateCompletionStreamReturnTimeoutError(t *testing.T) {
  function compareResponses (line 330) | func compareResponses(r1, r2 openai.CompletionResponse) bool {
  function compareResponseChoices (line 345) | func compareResponseChoices(c1, c2 openai.CompletionChoice) bool {

FILE: thread.go
  constant threadsSuffix (line 9) | threadsSuffix = "/threads"
  type Thread (line 12) | type Thread struct
  type ThreadRequest (line 22) | type ThreadRequest struct
  type ToolResources (line 28) | type ToolResources struct
  type CodeInterpreterToolResources (line 33) | type CodeInterpreterToolResources struct
  type FileSearchToolResources (line 37) | type FileSearchToolResources struct
  type ToolResourcesRequest (line 41) | type ToolResourcesRequest struct
  type CodeInterpreterToolResourcesRequest (line 46) | type CodeInterpreterToolResourcesRequest struct
  type FileSearchToolResourcesRequest (line 50) | type FileSearchToolResourcesRequest struct
  type VectorStoreToolResources (line 55) | type VectorStoreToolResources struct
  type ChunkingStrategy (line 61) | type ChunkingStrategy struct
  type StaticChunkingStrategy (line 66) | type StaticChunkingStrategy struct
  type ChunkingStrategyType (line 71) | type ChunkingStrategyType
  constant ChunkingStrategyTypeAuto (line 74) | ChunkingStrategyTypeAuto   ChunkingStrategyType = "auto"
  constant ChunkingStrategyTypeStatic (line 75) | ChunkingStrategyTypeStatic ChunkingStrategyType = "static"
  type ModifyThreadRequest (line 78) | type ModifyThreadRequest struct
  type ThreadMessageRole (line 83) | type ThreadMessageRole
  constant ThreadMessageRoleAssistant (line 86) | ThreadMessageRoleAssistant ThreadMessageRole = "assistant"
  constant ThreadMessageRoleUser (line 87) | ThreadMessageRoleUser      ThreadMessageRole = "user"
  type ThreadMessage (line 90) | type ThreadMessage struct
  type ThreadAttachment (line 98) | type ThreadAttachment struct
  type ThreadAttachmentTool (line 103) | type ThreadAttachmentTool struct
  type ThreadDeleteResponse (line 107) | type ThreadDeleteResponse struct
  method CreateThread (line 116) | func (c *Client) CreateThread(ctx context.Context, request ThreadRequest...
  method RetrieveThread (line 128) | func (c *Client) RetrieveThread(ctx context.Context, threadID string) (r...
  method ModifyThread (line 141) | func (c *Client) ModifyThread(
  method DeleteThread (line 158) | func (c *Client) DeleteThread(

FILE: thread_test.go
  function TestThread (line 15) | func TestThread(t *testing.T) {
  function TestAzureThread (line 98) | func TestAzureThread(t *testing.T) {

FILE: vector_store.go
  constant vectorStoresSuffix (line 11) | vectorStoresSuffix            = "/vector_stores"
  constant vectorStoresFilesSuffix (line 12) | vectorStoresFilesSuffix       = "/files"
  constant vectorStoresFileBatchesSuffix (line 13) | vectorStoresFileBatchesSuffix = "/file_batches"
  type VectorStoreFileCount (line 16) | type VectorStoreFileCount struct
  type VectorStore (line 24) | type VectorStore struct
  type VectorStoreExpires (line 39) | type VectorStoreExpires struct
  type VectorStoreRequest (line 45) | type VectorStoreRequest struct
  type VectorStoresList (line 53) | type VectorStoresList struct
  type VectorStoreDeleteResponse (line 61) | type VectorStoreDeleteResponse struct
  type VectorStoreFile (line 69) | type VectorStoreFile struct
  type VectorStoreFileRequest (line 80) | type VectorStoreFileRequest struct
  type VectorStoreFilesList (line 84) | type VectorStoreFilesList struct
  type VectorStoreFileBatch (line 93) | type VectorStoreFileBatch struct
  type VectorStoreFileBatchRequest (line 104) | type VectorStoreFileBatchRequest struct
  method CreateVectorStore (line 109) | func (c *Client) CreateVectorStore(ctx context.Context, request VectorSt...
  method RetrieveVectorStore (line 123) | func (c *Client) RetrieveVectorStore(
  method ModifyVectorStore (line 136) | func (c *Client) ModifyVectorStore(
  method DeleteVectorStore (line 150) | func (c *Client) DeleteVectorStore(
  method ListVectorStores (line 163) | func (c *Client) ListVectorStores(
  method CreateVectorStoreFile (line 196) | func (c *Client) CreateVectorStoreFile(
  method RetrieveVectorStoreFile (line 211) | func (c *Client) RetrieveVectorStoreFile(
  method DeleteVectorStoreFile (line 225) | func (c *Client) DeleteVectorStoreFile(
  method ListVectorStoreFiles (line 239) | func (c *Client) ListVectorStoreFiles(
  method CreateVectorStoreFileBatch (line 272) | func (c *Client) CreateVectorStoreFileBatch(
  method RetrieveVectorStoreFileBatch (line 287) | func (c *Client) RetrieveVectorStoreFileBatch(
  method CancelVectorStoreFileBatch (line 301) | func (c *Client) CancelVectorStoreFileBatch(
  method ListVectorStoreFilesInBatch (line 316) | func (c *Client) ListVectorStoreFilesInBatch(

FILE: vector_store_test.go
  function TestVectorStore (line 16) | func TestVectorStore(t *testing.T) {
Condensed preview — 105 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (606K chars).
[
  {
    "path": ".codecov.yml",
    "chars": 65,
    "preview": "coverage:\n  ignore:\n    - \"examples/**\"\n    - \"internal/test/**\"\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 81,
    "preview": "# These are supported funding model platforms\n\ngithub: [sashabaranov, vvatanabe]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 979,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\nYour issue may "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 749,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\nYour"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1295,
    "preview": "A similar PR may already be submitted!\nPlease search among the [Pull request](https://github.com/sashabaranov/go-openai/"
  },
  {
    "path": ".github/workflows/close-inactive-issues.yml",
    "chars": 753,
    "preview": "name: Close inactive issues\non:\n  schedule:\n    - cron: \"30 1 * * *\"\n\njobs:\n  close-issues:\n    runs-on: ubuntu-latest\n "
  },
  {
    "path": ".github/workflows/integration-tests.yml",
    "chars": 449,
    "preview": "name: Integration tests\n\non:\n  push:\n    branches:\n      - master\n\njobs:\n  integration_tests:\n    name: Run integration "
  },
  {
    "path": ".github/workflows/pr.yml",
    "chars": 672,
    "preview": "name: Sanity check\n\non:\n  - push\n  - pull_request\n\njobs:\n  prcheck:\n    name: Sanity check\n    runs-on: ubuntu-latest\n  "
  },
  {
    "path": ".gitignore",
    "chars": 343,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
  },
  {
    "path": ".golangci.yml",
    "chars": 3603,
    "preview": "version: \"2\"\nlinters:\n  default: none\n  enable:\n    - asciicheck\n    - bidichk\n    - bodyclose\n    - contextcheck\n    - "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4266,
    "preview": "# Contributing Guidelines\n\n## Overview\nThank you for your interest in contributing to the \"Go OpenAI\" project! By follow"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 23974,
    "preview": "# Go OpenAI\n[![Go Reference](https://pkg.go.dev/badge/github.com/sashabaranov/go-openai.svg)](https://pkg.go.dev/github."
  },
  {
    "path": "api_integration_test.go",
    "chars": 9243,
    "preview": "//go:build integration\n\npackage openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\n\t\"gi"
  },
  {
    "path": "api_internal_test.go",
    "chars": 4467,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"testing\"\n)\n\nfunc TestOpenAIFullURL(t *testing.T) {\n\tcases := []struct {\n\t\tName   s"
  },
  {
    "path": "assistant.go",
    "chars": 9444,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\nconst (\n\tassistantsSuffix      = \""
  },
  {
    "path": "assistant_test.go",
    "chars": 13642,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\n\topenai \"github.com/sashabaranov/go-openai\"\n\t\"github.com/sashabaranov/go-opena"
  },
  {
    "path": "audio.go",
    "chars": 6451,
    "preview": "package openai\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\n\tutils \"github.com/sashabaranov/go-openai/i"
  },
  {
    "path": "audio_api_test.go",
    "chars": 4097,
    "preview": "package openai_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"mime\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"path/filepath\""
  },
  {
    "path": "audio_test.go",
    "chars": 7215,
    "preview": "package openai //nolint:testpackage // testing private field\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/"
  },
  {
    "path": "batch.go",
    "chars": 7470,
    "preview": "package openai\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\nconst batchesSuffix = \"/b"
  },
  {
    "path": "batch_test.go",
    "chars": 10258,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai\"\n"
  },
  {
    "path": "chat.go",
    "chars": 19857,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"net/http\"\n\n\t\"github.com/sashabaranov/go-openai/jsonsche"
  },
  {
    "path": "chat_stream.go",
    "chars": 4150,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"net/http\"\n)\n\ntype ChatCompletionStreamChoiceDelta struct {\n\tContent      string   "
  },
  {
    "path": "chat_stream_test.go",
    "chars": 33714,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"gi"
  },
  {
    "path": "chat_test.go",
    "chars": 33550,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"tes"
  },
  {
    "path": "client.go",
    "chars": 8453,
    "preview": "package openai\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\tutils \"gi"
  },
  {
    "path": "client_test.go",
    "chars": 16709,
    "preview": "package openai //nolint:testpackage // testing private field\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/"
  },
  {
    "path": "common.go",
    "chars": 1089,
    "preview": "package openai\n\n// common.go defines common types used throughout the OpenAI API.\n\n// Usage Represents the total token u"
  },
  {
    "path": "completion.go",
    "chars": 10865,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"net/http\"\n)\n\n// GPT3 Defines the models provided by OpenAI to use when generating\n"
  },
  {
    "path": "completion_test.go",
    "chars": 9951,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"tes"
  },
  {
    "path": "config.go",
    "chars": 2722,
    "preview": "package openai\n\nimport (\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nconst (\n\topenaiAPIURLv1                 = \"https://api.open"
  },
  {
    "path": "config_test.go",
    "chars": 3290,
    "preview": "package openai_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai\"\n)\n\nfunc TestGetAzureDeploymentByModel(t *"
  },
  {
    "path": "edits.go",
    "chars": 1457,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// EditsRequest represents a request structure for Edits API.\n"
  },
  {
    "path": "edits_test.go",
    "chars": 2733,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/sash"
  },
  {
    "path": "embeddings.go",
    "chars": 11124,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\t\"net/http\""
  },
  {
    "path": "embeddings_test.go",
    "chars": 9927,
    "preview": "package openai_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"tes"
  },
  {
    "path": "engines.go",
    "chars": 1172,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// Engine struct represents engine from OpenAPI API.\ntype Engi"
  },
  {
    "path": "engines_test.go",
    "chars": 1512,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-op"
  },
  {
    "path": "error.go",
    "chars": 2852,
    "preview": "package openai\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// APIError provides error information returned by the Op"
  },
  {
    "path": "error_test.go",
    "chars": 8418,
    "preview": "package openai_test\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai\"\n)\n\nfunc "
  },
  {
    "path": "example_test.go",
    "chars": 8089,
    "preview": "package openai_test\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\""
  },
  {
    "path": "examples/README.md",
    "chars": 94,
    "preview": "To run an example:\n\n```\nexport OPENAI_API_KEY=\"<your key here>\"\ngo run ./example/<target>\n```\n"
  },
  {
    "path": "examples/chatbot/main.go",
    "chars": 949,
    "preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/sashabaranov/go-openai\"\n)\n\nfunc main() {\n\tclient :"
  },
  {
    "path": "examples/completion/main.go",
    "chars": 451,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/sashabaranov/go-openai\"\n)\n\nfunc main() {\n\tclient := openai."
  },
  {
    "path": "examples/completion-with-tool/main.go",
    "chars": 2768,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/sashabaranov/go-openai\"\n\t\"github.com/sashabaranov/go-openai"
  },
  {
    "path": "examples/images/main.go",
    "chars": 598,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/sashabaranov/go-openai\"\n)\n\nfunc main() {\n\tclient := openai."
  },
  {
    "path": "examples/voice-to-text/main.go",
    "chars": 654,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/sashabaranov/go-openai\"\n)\n\nfunc main() {\n\tif len("
  },
  {
    "path": "files.go",
    "chars": 4132,
    "preview": "package openai\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n)\n\ntype FileRequest struct {\n\tFileName string `jso"
  },
  {
    "path": "files_api_test.go",
    "chars": 5501,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"testing\""
  },
  {
    "path": "files_test.go",
    "chars": 4260,
    "preview": "package openai //nolint:testpackage // testing private field\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\n\tutils "
  },
  {
    "path": "fine_tunes.go",
    "chars": 7738,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// Deprecated: On August 22nd, 2023, OpenAI announced the depr"
  },
  {
    "path": "fine_tunes_test.go",
    "chars": 2161,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-op"
  },
  {
    "path": "fine_tuning_job.go",
    "chars": 4354,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\ntype FineTuningJob struct {\n\tID              string"
  },
  {
    "path": "fine_tuning_job_test.go",
    "chars": 3110,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-op"
  },
  {
    "path": "go.mod",
    "chars": 50,
    "preview": "module github.com/sashabaranov/go-openai\n\ngo 1.18\n"
  },
  {
    "path": "image.go",
    "chars": 7848,
    "preview": "package openai\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n)\n\n// Image sizes defined by the OpenAI API.\nc"
  },
  {
    "path": "image_api_test.go",
    "chars": 5802,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"t"
  },
  {
    "path": "image_test.go",
    "chars": 9804,
    "preview": "package openai //nolint:testpackage // testing private field\n\nimport (\n\tutils \"github.com/sashabaranov/go-openai/interna"
  },
  {
    "path": "internal/error_accumulator.go",
    "chars": 693,
    "preview": "package openai\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype ErrorAccumulator interface {\n\tWrite(p []byte) error\n\tBytes() []by"
  },
  {
    "path": "internal/error_accumulator_test.go",
    "chars": 1271,
    "preview": "package openai_test\n\nimport (\n\t\"testing\"\n\n\topenai \"github.com/sashabaranov/go-openai/internal\"\n\t\"github.com/sashabaranov"
  },
  {
    "path": "internal/form_builder.go",
    "chars": 2490,
    "preview": "package openai\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/textproto\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\ntype FormB"
  },
  {
    "path": "internal/form_builder_test.go",
    "chars": 5208,
    "preview": "package openai //nolint:testpackage // testing private field\n\nimport (\n\t\"errors\"\n\t\"io\"\n\n\t\"github.com/sashabaranov/go-ope"
  },
  {
    "path": "internal/marshaller.go",
    "chars": 235,
    "preview": "package openai\n\nimport (\n\t\"encoding/json\"\n)\n\ntype Marshaller interface {\n\tMarshal(value any) ([]byte, error)\n}\n\ntype JSO"
  },
  {
    "path": "internal/marshaller_test.go",
    "chars": 814,
    "preview": "package openai_test\n\nimport (\n\t\"testing\"\n\n\topenai \"github.com/sashabaranov/go-openai/internal\"\n\t\"github.com/sashabaranov"
  },
  {
    "path": "internal/request_builder.go",
    "chars": 946,
    "preview": "package openai\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n)\n\ntype RequestBuilder interface {\n\tBuild(ctx context.Con"
  },
  {
    "path": "internal/request_builder_test.go",
    "chars": 2621,
    "preview": "package openai //nolint:testpackage // testing private field\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t"
  },
  {
    "path": "internal/test/checks/checks.go",
    "chars": 962,
    "preview": "package checks\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\nfunc NoError(t *testing.T, err error, message ...string) {\n\tt.Helper()\n"
  },
  {
    "path": "internal/test/checks/checks_test.go",
    "chars": 454,
    "preview": "package checks_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai/internal/test/checks\"\n)\n\nfunc Te"
  },
  {
    "path": "internal/test/failer.go",
    "chars": 390,
    "preview": "package test\n\nimport \"errors\"\n\nvar (\n\tErrTestErrorAccumulatorWriteFailed = errors.New(\"test error accumulator failed\")\n)"
  },
  {
    "path": "internal/test/failer_test.go",
    "chars": 567,
    "preview": "//nolint:testpackage // need access to unexported fields and types for testing\npackage test\n\nimport (\n\t\"errors\"\n\t\"testin"
  },
  {
    "path": "internal/test/helpers.go",
    "chars": 1408,
    "preview": "package test\n\nimport (\n\t\"github.com/sashabaranov/go-openai/internal/test/checks\"\n\n\t\"net/http\"\n\t\"os\"\n\t\"testing\"\n)\n\n// Cre"
  },
  {
    "path": "internal/test/helpers_test.go",
    "chars": 1389,
    "preview": "package test_test\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\tinternaltest \"git"
  },
  {
    "path": "internal/test/server.go",
    "chars": 1971,
    "preview": "package test\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nconst testAPI = \"this-is-my-secur"
  },
  {
    "path": "internal/test/server_test.go",
    "chars": 2123,
    "preview": "package test_test\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\tinternaltest \"github.com/sashabaranov/go-openai/internal/test"
  },
  {
    "path": "internal/unmarshaler.go",
    "chars": 244,
    "preview": "package openai\n\nimport (\n\t\"encoding/json\"\n)\n\ntype Unmarshaler interface {\n\tUnmarshal(data []byte, v any) error\n}\n\ntype J"
  },
  {
    "path": "internal/unmarshaler_test.go",
    "chars": 868,
    "preview": "package openai_test\n\nimport (\n\t\"testing\"\n\n\topenai \"github.com/sashabaranov/go-openai/internal\"\n\t\"github.com/sashabaranov"
  },
  {
    "path": "jsonschema/containsref_test.go",
    "chars": 1298,
    "preview": "package jsonschema_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai/jsonschema\"\n)\n\n// SelfRef struct used "
  },
  {
    "path": "jsonschema/json.go",
    "chars": 6687,
    "preview": "// Package jsonschema provides very simple functionality for representing a JSON schema as a\n// (nested) struct. This st"
  },
  {
    "path": "jsonschema/json_additional_test.go",
    "chars": 2257,
    "preview": "package jsonschema_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai/jsonschema\"\n)\n\n// Test Definition.Unma"
  },
  {
    "path": "jsonschema/json_errors_test.go",
    "chars": 612,
    "preview": "package jsonschema_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai/jsonschema\"\n)\n\n// TestGenerateSchemaFo"
  },
  {
    "path": "jsonschema/json_test.go",
    "chars": 12997,
    "preview": "package jsonschema_test\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai/jsonschema"
  },
  {
    "path": "jsonschema/validate.go",
    "chars": 3147,
    "preview": "package jsonschema\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n)\n\nfunc CollectDefs(def Definition) map[string]Definition {\n\tres"
  },
  {
    "path": "jsonschema/validate_test.go",
    "chars": 11126,
    "preview": "package jsonschema_test\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-openai/jsonschema\"\n)\n\nfunc Test_Va"
  },
  {
    "path": "messages.go",
    "chars": 5876,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\nconst (\n\tmessagesSuffix = \"messages\"\n)\n\ntype Messag"
  },
  {
    "path": "messages_test.go",
    "chars": 7547,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/sashabaranov/go-op"
  },
  {
    "path": "models.go",
    "chars": 2728,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// Model struct represents an OpenAPI model.\ntype Model struct"
  },
  {
    "path": "models_test.go",
    "chars": 3885,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/sash"
  },
  {
    "path": "moderation.go",
    "chars": 3913,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n)\n\n// The moderation endpoint is a tool you can use to check w"
  },
  {
    "path": "moderation_test.go",
    "chars": 5275,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"ti"
  },
  {
    "path": "openai_test.go",
    "chars": 1161,
    "preview": "package openai_test\n\nimport (\n\t\"github.com/sashabaranov/go-openai\"\n\t\"github.com/sashabaranov/go-openai/internal/test\"\n)\n"
  },
  {
    "path": "ratelimit.go",
    "chars": 1384,
    "preview": "package openai\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// RateLimitHeaders struct represents Openai rate limits head"
  },
  {
    "path": "reasoning_validator.go",
    "chars": 3713,
    "preview": "package openai\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\nvar (\n\t// Deprecated: use ErrReasoningModelMaxTokensDeprecated instead."
  },
  {
    "path": "run.go",
    "chars": 13349,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\ntype Run struct {\n\tID             string           "
  },
  {
    "path": "run_test.go",
    "chars": 5841,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\n\topenai \"github.com/sashabaranov/go-openai\"\n\t\"github.com/sashabaranov/go-opena"
  },
  {
    "path": "speech.go",
    "chars": 1912,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"net/http\"\n)\n\ntype SpeechModel string\n\nconst (\n\tTTSModel1         SpeechModel = \"tt"
  },
  {
    "path": "speech_test.go",
    "chars": 2507,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"test"
  },
  {
    "path": "stream.go",
    "chars": 1224,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n)\n\nvar (\n\tErrTooManyEmptyStreamMessages = errors.New(\"stream h"
  },
  {
    "path": "stream_reader.go",
    "chars": 2384,
    "preview": "package openai\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"regexp\"\n\n\tutils \"github.com/sashabaranov/go-openai"
  },
  {
    "path": "stream_reader_test.go",
    "chars": 2402,
    "preview": "package openai //nolint:testpackage // testing private field\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"testing\"\n\n\tutils \"g"
  },
  {
    "path": "stream_test.go",
    "chars": 11252,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github"
  },
  {
    "path": "thread.go",
    "chars": 4747,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"net/http\"\n)\n\nconst (\n\tthreadsSuffix = \"/threads\"\n)\n\ntype Thread struct {\n\tID      "
  },
  {
    "path": "thread_test.go",
    "chars": 4488,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\topenai \"github.com/sashabarano"
  },
  {
    "path": "vector_store.go",
    "chars": 10509,
    "preview": "package openai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\nconst (\n\tvectorStoresSuffix            = \"/vector_s"
  },
  {
    "path": "vector_store_test.go",
    "chars": 10237,
    "preview": "package openai_test\n\nimport (\n\t\"context\"\n\n\topenai \"github.com/sashabaranov/go-openai\"\n\t\"github.com/sashabaranov/go-opena"
  }
]

About this extraction

This page contains the full source code of the sashabaranov/go-openai GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 105 files (529.2 KB), approximately 144.9k tokens, and a symbol index with 970 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!