Repository: sashabaranov/go-openai
Pull request #906 (2024-11-23 16:21:22)

Support for extra_body parameter for embeddings API
**Describe the change**
This change adds support for an ExtraBody parameter in the EmbeddingRequest struct, allowing developers to include additional, arbitrary key-value pairs in the API request payload. This ensures extensibility and flexibility when working with the OpenAI Embeddings API, especially for experimental features, future API updates, or custom parameters that are not explicitly defined in the current request structure. The addition of ExtraBody is particularly valuable for supporting LLM engines like vLLM, Nvidia NIM, etc, which may require additional fields beyond the standard parameters. [Refrence]( https://build.nvidia.com/nvidia/nv-embedqa-e5-v5?snippet_tab=Python)
There is an issue created that ExtraBody parameter support is required for vLLM engine also. https://github.com/sashabaranov/go-openai/issues/898
In this PR ExtraBody parameter support is added only for embedding API but it can be extended for other APIs as well like chat completions

**Provide OpenAI documentation link**
OpenAI's official python client also supports this extra_body field which this go client could also support. [Refrence](https://github.com/openai/openai-python/blob/83091e96cf43f344d22799c22eea301aeae36d51/src/openai/resources/embeddings.py#L58C9-L58C19)

**Describe your solution**
The solution involves:
Introducing the ExtraBody field in the EmbeddingRequest struct. This is a map of string keys to arbitrary values (map[string]any) that allows developers to pass additional fields to the API dynamically.
Updating the CreateEmbeddings method to merge the ExtraBody values into the main request payload (body) before sending it to the API. This is done through the withExtraBody request option, which ensures the fields in ExtraBody are included without overwriting existing mandatory parameters.

**Tests**
Added unit tests and done sanity check of embeddings api with extra_body param for Nvidia NIM embedding model


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sashabaranov/go-openai/pull/906?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov) Report
Attention: Patch coverage is `61.11111%` with `14 lines` in your changes missing coverage. Please review.
> Project coverage is 97.93%. Comparing base [(`774fc9d`)](https://app.codecov.io/gh/sashabaranov/go-openai/commit/774fc9dd12ed60c10a9f9f03319ddb9cd5f8780c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov) to head [(`5b15527`)](https://app.codecov.io/gh/sashabaranov/go-openai/commit/5b15527672e324d5c4e55ff4f3d891fb4774a89b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov).
> Report is 73 commits behind head on master.

| [Files with missing lines](https://app.codecov.io/gh/sashabaranov/go-openai/pull/906?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov) | Patch % | Lines |
|---|---|---|
| [client.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/906?src=pr&el=tree&filepath=client.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov#diff-Y2xpZW50Lmdv) | 26.66% | [8 Missing and 3 partials :warning: ](https://app.codecov.io/gh/sashabaranov/go-openai/pull/906?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov) |
| [embeddings.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/906?src=pr&el=tree&filepath=embeddings.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov#diff-ZW1iZWRkaW5ncy5nbw==) | 85.71% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/sashabaranov/go-openai/pull/906?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #906      +/-   ##
==========================================
- Coverage   98.46%   97.93%   -0.53%     
==========================================
  Files          24       26       +2     
  Lines        1364     1793     +429     
==========================================
+ Hits         1343     1756     +413     
- Misses         15       26      +11     
- Partials        6       11       +5     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/sashabaranov/go-openai/pull/906?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov).

----
🚨 Try these New Features:

- [Flaky Tests Detection](https://docs.codecov.com/docs/test-result-ingestion-beta) - Detect and resolve failed and flaky tests
AyushSawant18588: @sashabaranov Can you please look into this?

Patch:
diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..8177366
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,4 @@
+coverage:
+  ignore:
+    - "examples/**"
+    - "internal/test/**"
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index a41fff9..2c97306 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -13,15 +13,17 @@ jobs:
     - name: Setup Go
       uses: actions/setup-go@v5
       with:
-        go-version: '1.21'
+        go-version: '1.24'
     - name: Run vet
       run: |
-        go vet .
+        go vet -stdversion ./...
     - name: Run golangci-lint
-      uses: golangci/golangci-lint-action@v4
+      uses: golangci/golangci-lint-action@v7
       with:
-        version: latest
+        version: v2.1.5
     - name: Run tests
-      run: go test -race -covermode=atomic -coverprofile=coverage.out -v .
+      run: go test -race -covermode=atomic -coverprofile=coverage.out -v ./...
     - name: Upload coverage reports to Codecov
-      uses: codecov/codecov-action@v4
+      uses: codecov/codecov-action@v5
+      with:
+        token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.golangci.yml b/.golangci.yml
index 724cb73..6391ad7 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,272 +1,168 @@
-## Golden config for golangci-lint v1.47.3
-#
-# This is the best config for golangci-lint based on my experience and opinion.
-# It is very strict, but not extremely strict.
-# Feel free to adopt and change it for your needs.
-
-run:
-  # Timeout for analysis, e.g. 30s, 5m.
-  # Default: 1m
-  timeout: 3m
-
-
-# This file contains only configs which differ from defaults.
-# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
-linters-settings:
-  cyclop:
-    # The maximal code complexity to report.
-    # Default: 10
-    max-complexity: 30
-    # The maximal average package complexity.
-    # If it's higher than 0.0 (float) the check is enabled
-    # Default: 0.0
-    package-average: 10.0
-
-  errcheck:
-    # Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
-    # Such cases aren't reported by default.
-    # Default: false
-    check-type-assertions: true
-
-  funlen:
-    # Checks the number of lines in a function.
-    # If lower than 0, disable the check.
-    # Default: 60
-    lines: 100
-    # Checks the number of statements in a function.
-    # If lower than 0, disable the check.
-    # Default: 40
-    statements: 50
-
-  gocognit:
-    # Minimal code complexity to report
-    # Default: 30 (but we recommend 10-20)
-    min-complexity: 20
-
-  gocritic:
-    # Settings passed to gocritic.
-    # The settings key is the name of a supported gocritic checker.
-    # The list of supported checkers can be find in https://go-critic.github.io/overview.
-    settings:
-      captLocal:
-        # Whether to restrict checker to params only.
-        # Default: true
-        paramsOnly: false
-      underef:
-        # Whether to skip (*x).method() calls where x is a pointer receiver.
-        # Default: true
-        skipRecvDeref: false
-
-  mnd:
-    # List of function patterns to exclude from analysis.
-    # Values always ignored: `time.Date`
-    # Default: []
-    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
-
-  gomodguard:
-    blocked:
-      # List of blocked modules.
-      # Default: []
-      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:
-    # Enable all analyzers.
-    # Default: false
-    enable-all: true
-    # Disable analyzers by name.
-    # Run `go tool vet help` to see all analyzers.
-    # Default: []
-    disable:
-      - fieldalignment # too strict
-    # Settings per analyzer.
-    settings:
-      shadow:
-        # Whether to be strict about shadowing; can be noisy.
-        # Default: false
-        strict: true
-
-  nakedret:
-    # Make an issue if func has more lines of code than this setting, and it has naked returns.
-    # Default: 30
-    max-func-lines: 0
-
-  nolintlint:
-    # Exclude following linters from requiring an explanation.
-    # Default: []
-    allow-no-explanation: [ funlen, gocognit, lll ]
-    # Enable to require an explanation of nonzero length after each nolint directive.
-    # Default: false
-    require-explanation: true
-    # Enable to require nolint directives to mention the specific linter being suppressed.
-    # Default: false
-    require-specific: true
-
-  rowserrcheck:
-    # database/sql is always checked
-    # Default: []
-    packages:
-      - github.com/jmoiron/sqlx
-
-  tenv:
-    # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
-    # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
-    # Default: false
-    all: true
-
-  varcheck:
-    # Check usage of exported fields and variables.
-    # Default: false
-    exported-fields: false # default false # TODO: enable after fixing false positives
-
-
+version: "2"
 linters:
-  disable-all: true
+  default: none
   enable:
-    ## enabled by default
-    - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
-    - gosimple # Linter for Go source code that specializes in simplifying a code
-    - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
-    - ineffassign # Detects when assignments to existing variables are not used
-    - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
-    - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
-    - unused # Checks Go code for unused constants, variables, functions and types
-    ## disabled by default
-    # - asasalint # Check for pass []any as any in variadic func(...any)
-    - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
-    - bidichk # Checks for dangerous unicode character sequences
-    - bodyclose # checks whether HTTP response body is closed successfully
-    - contextcheck # check the function whether use a non-inherited context
-    - cyclop # checks function and package cyclomatic complexity
-    - dupl # Tool for code clone detection
-    - durationcheck # check for two durations multiplied together
-    - errname # Checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error.
-    - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
-    # Removed execinquery (deprecated). execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds
-    - exhaustive # check exhaustiveness of enum switch statements
-    - exportloopref # checks for pointers to enclosing loop variables
-    - forbidigo # Forbids identifiers
-    - funlen # Tool for detection of long functions
-    # - gochecknoglobals # check that no global variables exist
-    - gochecknoinits # Checks that no init functions are present in Go code
-    - gocognit # Computes and checks the cognitive complexity of functions
-    - goconst # Finds repeated strings that could be replaced by a constant
-    - gocritic # Provides diagnostics that check for bugs, performance and style issues.
-    - gocyclo # Computes and checks the cyclomatic complexity of functions
-    - godot # Check if comments end in a period
-    - goimports # In addition to fixing imports, goimports also formats your code in the same style as gofmt.
-    - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
-    - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
-    - goprintffuncname # Checks that printf-like functions are named with f at the end
-    - gosec # Inspects source code for security problems
-    - lll # Reports long lines
-    - makezero # Finds slice declarations with non-zero initial length
-    # - nakedret # Finds naked returns in functions greater than a specified function length
-    - mnd # An analyzer to detect magic numbers.
-    - nestif # Reports deeply nested if statements
-    - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
-    - nilnil # Checks that there is no simultaneous return of nil error and an invalid value.
-    # - noctx # noctx finds sending http request without context.Context
-    - nolintlint # Reports ill-formed or insufficient nolint directives
-    # - nonamedreturns # Reports all named returns
-    - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL.
-    - predeclared # find code that shadows one of Go's predeclared identifiers
-    - promlinter # Check Prometheus metrics naming via promlint
-    - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.
-    - rowserrcheck # checks whether Err of rows is checked successfully
-    - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
-    - stylecheck # Stylecheck is a replacement for golint
-    - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
-    - testpackage # linter that makes you use a separate _test package
-    - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
-    - unconvert # Remove unnecessary type conversions
-    - unparam # Reports unused function parameters
-    - wastedassign # wastedassign finds wasted assignment statements.
-    - whitespace # Tool for detection of leading and trailing whitespace
-    ## you may want to enable
-    #- decorder # check declaration order and count of types, constants, variables and functions
-    #- exhaustruct # Checks if all structure fields are initialized
-    #- goheader # Checks is file header matches to pattern
-    #- ireturn # Accept Interfaces, Return Concrete Types
-    #- prealloc # [premature optimization, but can be used in some cases] Finds slice declarations that could potentially be preallocated
-    #- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope
-    #- wrapcheck # Checks that errors returned from external packages are wrapped
-    ## disabled
-    #- containedctx # containedctx is a linter that detects struct contained context.Context field
-    #- depguard # [replaced by gomodguard] Go linter that checks if package imports are in a list of acceptable packages
-    #- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
-    #- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted.
-    #- forcetypeassert # [replaced by errcheck] finds forced type assertions
-    #- gci # Gci controls golang package import order and makes it always deterministic.
-    #- godox # Tool for detection of FIXME, TODO and other comment keywords
-    #- goerr113 # [too strict] Golang linter to check the errors handling expressions
-    #- gofmt # [replaced by goimports] Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
-    #- gofumpt # [replaced by goimports, gofumports is not available yet] Gofumpt checks whether code was gofumpt-ed.
-    #- grouper # An analyzer to analyze expression groups.
-    #- ifshort # Checks that your code uses short syntax for if-statements whenever possible
-    #- importas # Enforces consistent import aliases
-    #- maintidx # maintidx measures the maintainability index of each function.
-    #- misspell # [useless] Finds commonly misspelled English words in comments
-    #- nlreturn # [too strict and mostly code is not more readable] nlreturn checks for a new line before return and branch statements to increase code clarity
-    #- nosnakecase # Detects snake case of variable naming and function name. # TODO: maybe enable after https://github.com/sivchari/nosnakecase/issues/14
-    #- paralleltest # [too many false positives] paralleltest detects missing usage of t.Parallel() method in your Go test
-    #- tagliatelle # Checks the struct tags.
-    #- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
-    #- wsl # [too strict and mostly code is not more readable] Whitespace Linter - Forces you to use empty lines!
-    ## deprecated
-    #- exhaustivestruct # [deprecated, replaced by exhaustruct] Checks if all struct's fields are initialized
-    #- golint # [deprecated, replaced by revive] Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
-    #- interfacer # [deprecated] Linter that suggests narrower interface types
-    #- maligned # [deprecated, replaced by govet fieldalignment] Tool to detect Go structs that would take less memory if their fields were sorted
-    #- scopelint # [deprecated, replaced by exportloopref] Scopelint checks for unpinned variables in go programs
-
-
+    - 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:
-  # Maximum count of issues with the same text.
-  # Set to 0 to disable.
-  # Default: 3
   max-same-issues: 50
-
-  exclude-rules:
-    - source: "^//\\s*go:generate\\s"
-      linters: [ lll ]
-    - source: "(noinspection|TODO)"
-      linters: [ godot ]
-    - source: "//noinspection"
-      linters: [ gocritic ]
-    - source: "^\\s+if _, ok := err\\.\\([^.]+\\.InternalError\\); ok {"
-      linters: [ errorlint ]
-    - path: "_test\\.go"
-      linters:
-        - bodyclose
-        - dupl
-        - funlen
-        - goconst
-        - gosec
-        - noctx
-        - wrapcheck
+formatters:
+  enable:
+    - goimports
+  exclusions:
+    generated: lax
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
diff --git a/README.md b/README.md
index 57d1d35..77b85e5 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ This library provides unofficial Go clients for [OpenAI API](https://platform.op
 
 * ChatGPT 4o, o1
 * GPT-3, GPT-4
-* DALL·E 2, DALL·E 3
+* DALL·E 2, DALL·E 3, GPT Image 1
 * Whisper
 
 ## Installation
@@ -357,6 +357,66 @@ func main() {
 ```
 </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>
 
diff --git a/chat.go b/chat.go
index fcaf79c..0f0c5b5 100644
--- a/chat.go
+++ b/chat.go
@@ -5,6 +5,8 @@ import (
 	"encoding/json"
 	"errors"
 	"net/http"
+
+	"github.com/sashabaranov/go-openai/jsonschema"
 )
 
 // Chat message role defined by the OpenAI API.
@@ -14,6 +16,7 @@ const (
 	ChatMessageRoleAssistant = "assistant"
 	ChatMessageRoleFunction  = "function"
 	ChatMessageRoleTool      = "tool"
+	ChatMessageRoleDeveloper = "developer"
 )
 
 const chatCompletionsSuffix = "/chat/completions"
@@ -93,7 +96,7 @@ type ChatMessagePart struct {
 
 type ChatCompletionMessage struct {
 	Role         string `json:"role"`
-	Content      string `json:"content"`
+	Content      string `json:"content,omitempty"`
 	Refusal      string `json:"refusal,omitempty"`
 	MultiContent []ChatMessagePart
 
@@ -103,6 +106,12 @@ type ChatCompletionMessage struct {
 	// - 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.
@@ -118,41 +127,44 @@ func (m ChatCompletionMessage) MarshalJSON() ([]byte, error) {
 	}
 	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"`
-			FunctionCall *FunctionCall     `json:"function_call,omitempty"`
-			ToolCalls    []ToolCall        `json:"tool_calls,omitempty"`
-			ToolCallID   string            `json:"tool_call_id,omitempty"`
+			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"`
-		Refusal      string            `json:"refusal,omitempty"`
-		MultiContent []ChatMessagePart `json:"-"`
-		Name         string            `json:"name,omitempty"`
-		FunctionCall *FunctionCall     `json:"function_call,omitempty"`
-		ToolCalls    []ToolCall        `json:"tool_calls,omitempty"`
-		ToolCallID   string            `json:"tool_call_id,omitempty"`
+		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"`
-		FunctionCall *FunctionCall `json:"function_call,omitempty"`
-		ToolCalls    []ToolCall    `json:"tool_calls,omitempty"`
-		ToolCallID   string        `json:"tool_call_id,omitempty"`
+		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 {
@@ -160,14 +172,15 @@ func (m *ChatCompletionMessage) UnmarshalJSON(bs []byte) error {
 		return nil
 	}
 	multiMsg := struct {
-		Role         string `json:"role"`
-		Content      string
-		Refusal      string            `json:"refusal,omitempty"`
-		MultiContent []ChatMessagePart `json:"content"`
-		Name         string            `json:"name,omitempty"`
-		FunctionCall *FunctionCall     `json:"function_call,omitempty"`
-		ToolCalls    []ToolCall        `json:"tool_calls,omitempty"`
-		ToolCallID   string            `json:"tool_call_id,omitempty"`
+		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
@@ -210,6 +223,31 @@ type ChatCompletionResponseFormatJSONSchema struct {
 	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
+}
+
 // ChatCompletionRequest represents a request structure for chat completion API.
 type ChatCompletionRequest struct {
 	Model    string                  `json:"model"`
@@ -258,8 +296,19 @@ type ChatCompletionRequest struct {
 	// 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"`
 }
 
 type StreamOptions struct {
@@ -327,6 +376,11 @@ type LogProbs struct {
 	Content []LogProb `json:"content"`
 }
 
+type Prediction struct {
+	Content string `json:"content"`
+	Type    string `json:"type"`
+}
+
 type FinishReason string
 
 const (
@@ -338,6 +392,15 @@ const (
 	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
@@ -370,6 +433,7 @@ type ChatCompletionResponse struct {
 	Usage               Usage                  `json:"usage"`
 	SystemFingerprint   string                 `json:"system_fingerprint"`
 	PromptFilterResults []PromptFilterResult   `json:"prompt_filter_results,omitempty"`
+	ServiceTier         ServiceTier            `json:"service_tier,omitempty"`
 
 	httpHeader
 }
@@ -390,7 +454,8 @@ func (c *Client) CreateChatCompletion(
 		return
 	}
 
-	if err = validateRequestForO1Models(request); err != nil {
+	reasoningValidator := NewReasoningValidator()
+	if err = reasoningValidator.Validate(request); err != nil {
 		return
 	}
 
diff --git a/chat_stream.go b/chat_stream.go
index 58b2651..80d16cc 100644
--- a/chat_stream.go
+++ b/chat_stream.go
@@ -11,6 +11,12 @@ type ChatCompletionStreamChoiceDelta struct {
 	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 {
@@ -80,7 +86,8 @@ func (c *Client) CreateChatCompletionStream(
 	}
 
 	request.Stream = true
-	if err = validateRequestForO1Models(request); err != nil {
+	reasoningValidator := NewReasoningValidator()
+	if err = reasoningValidator.Validate(request); err != nil {
 		return
 	}
 
diff --git a/client.go b/client.go
index ed8595e..413b8db 100644
--- a/client.go
+++ b/client.go
@@ -84,6 +84,20 @@ func withBody(body any) requestOption {
 	}
 }
 
+func withExtraBody(extraBody map[string]any) requestOption {
+	return func(args *requestOptions) {
+		// Assert that args.body is a map[string]any.
+		bodyMap, ok := args.body.(map[string]any)
+		if ok {
+			// If it's a map[string]any then only add extraBody
+			// fields to args.body otherwise keep only fields in request struct.
+			for key, value := range extraBody {
+				bodyMap[key] = value
+			}
+		}
+	}
+}
+
 func withContentType(contentType string) requestOption {
 	return func(args *requestOptions) {
 		args.header.Set("Content-Type", contentType)
@@ -182,13 +196,21 @@ func sendRequestStream[T streamable](client *Client, req *http.Request) (*stream
 
 func (c *Client) setCommonHeaders(req *http.Request) {
 	// https://learn.microsoft.com/en-us/azure/cognitive-services/openai/reference#authentication
-	// Azure API Key authentication
-	if c.config.APIType == APITypeAzure || c.config.APIType == APITypeCloudflareAzure {
+	switch c.config.APIType {
+	case APITypeAzure, APITypeCloudflareAzure:
+		// Azure API Key authentication
 		req.Header.Set(AzureAPIKeyHeader, c.config.authToken)
-	} else if c.config.authToken != "" {
-		// OpenAI or Azure AD authentication
-		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.config.authToken))
+	case APITypeAnthropic:
+		// https://docs.anthropic.com/en/api/versioning
+		req.Header.Set("anthropic-version", c.config.APIVersion)
+	case APITypeOpenAI, APITypeAzureAD:
+		fallthrough
+	default:
+		if c.config.authToken != "" {
+			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.config.authToken))
+		}
 	}
+
 	if c.config.OrgID != "" {
 		req.Header.Set("OpenAI-Organization", c.config.OrgID)
 	}
diff --git a/common.go b/common.go
index 8cc7289..d1936d6 100644
--- a/common.go
+++ b/common.go
@@ -13,8 +13,10 @@ type Usage struct {
 
 // CompletionTokensDetails Breakdown of tokens used in a completion.
 type CompletionTokensDetails struct {
-	AudioTokens     int `json:"audio_tokens"`
-	ReasoningTokens int `json:"reasoning_tokens"`
+	AudioTokens              int `json:"audio_tokens"`
+	ReasoningTokens          int `json:"reasoning_tokens"`
+	AcceptedPredictionTokens int `json:"accepted_prediction_tokens"`
+	RejectedPredictionTokens int `json:"rejected_prediction_tokens"`
 }
 
 // PromptTokensDetails Breakdown of tokens used in the prompt.
diff --git a/completion.go b/completion.go
index f115660..02ce7b0 100644
--- a/completion.go
+++ b/completion.go
@@ -2,60 +2,61 @@ package openai
 
 import (
 	"context"
-	"errors"
 	"net/http"
 )
 
-var (
-	ErrO1MaxTokensDeprecated                   = errors.New("this model is not supported MaxTokens, please use MaxCompletionTokens")                               //nolint:lll
-	ErrCompletionUnsupportedModel              = errors.New("this model is not supported with this method, please use CreateChatCompletion client method instead") //nolint:lll
-	ErrCompletionStreamNotSupported            = errors.New("streaming is not supported with this method, please use CreateCompletionStream")                      //nolint:lll
-	ErrCompletionRequestPromptTypeNotSupported = errors.New("the type of CompletionRequest.Prompt only supports string and []string")                              //nolint:lll
-)
-
-var (
-	ErrO1BetaLimitationsMessageTypes = errors.New("this model has beta-limitations, user and assistant messages only, system messages are not supported")                                  //nolint:lll
-	ErrO1BetaLimitationsTools        = errors.New("this model has beta-limitations, tools, function calling, and response format parameters are not supported")                            //nolint:lll
-	ErrO1BetaLimitationsLogprobs     = errors.New("this model has beta-limitations, logprobs not supported")                                                                               //nolint:lll
-	ErrO1BetaLimitationsOther        = errors.New("this model has beta-limitations, temperature, top_p and n are fixed at 1, while presence_penalty and frequency_penalty are fixed at 0") //nolint:lll
-)
-
 // GPT3 Defines the models provided by OpenAI to use when generating
 // completions from OpenAI.
 // GPT3 Models are designed for text-based tasks. For code-specific
 // tasks, please refer to the Codex series of models.
 const (
-	O1Mini                = "o1-mini"
-	O1Mini20240912        = "o1-mini-2024-09-12"
-	O1Preview             = "o1-preview"
-	O1Preview20240912     = "o1-preview-2024-09-12"
-	GPT432K0613           = "gpt-4-32k-0613"
-	GPT432K0314           = "gpt-4-32k-0314"
-	GPT432K               = "gpt-4-32k"
-	GPT40613              = "gpt-4-0613"
-	GPT40314              = "gpt-4-0314"
-	GPT4o                 = "gpt-4o"
-	GPT4o20240513         = "gpt-4o-2024-05-13"
-	GPT4o20240806         = "gpt-4o-2024-08-06"
-	GPT4o20241120         = "gpt-4o-2024-11-20"
-	GPT4oLatest           = "chatgpt-4o-latest"
-	GPT4oMini             = "gpt-4o-mini"
-	GPT4oMini20240718     = "gpt-4o-mini-2024-07-18"
-	GPT4Turbo             = "gpt-4-turbo"
-	GPT4Turbo20240409     = "gpt-4-turbo-2024-04-09"
-	GPT4Turbo0125         = "gpt-4-0125-preview"
-	GPT4Turbo1106         = "gpt-4-1106-preview"
-	GPT4TurboPreview      = "gpt-4-turbo-preview"
-	GPT4VisionPreview     = "gpt-4-vision-preview"
-	GPT4                  = "gpt-4"
-	GPT3Dot5Turbo0125     = "gpt-3.5-turbo-0125"
-	GPT3Dot5Turbo1106     = "gpt-3.5-turbo-1106"
-	GPT3Dot5Turbo0613     = "gpt-3.5-turbo-0613"
-	GPT3Dot5Turbo0301     = "gpt-3.5-turbo-0301"
-	GPT3Dot5Turbo16K      = "gpt-3.5-turbo-16k"
-	GPT3Dot5Turbo16K0613  = "gpt-3.5-turbo-16k-0613"
-	GPT3Dot5Turbo         = "gpt-3.5-turbo"
-	GPT3Dot5TurboInstruct = "gpt-3.5-turbo-instruct"
+	O1Mini                  = "o1-mini"
+	O1Mini20240912          = "o1-mini-2024-09-12"
+	O1Preview               = "o1-preview"
+	O1Preview20240912       = "o1-preview-2024-09-12"
+	O1                      = "o1"
+	O120241217              = "o1-2024-12-17"
+	O3                      = "o3"
+	O320250416              = "o3-2025-04-16"
+	O3Mini                  = "o3-mini"
+	O3Mini20250131          = "o3-mini-2025-01-31"
+	O4Mini                  = "o4-mini"
+	O4Mini20250416          = "o4-mini-2025-04-16"
+	GPT432K0613             = "gpt-4-32k-0613"
+	GPT432K0314             = "gpt-4-32k-0314"
+	GPT432K                 = "gpt-4-32k"
+	GPT40613                = "gpt-4-0613"
+	GPT40314                = "gpt-4-0314"
+	GPT4o                   = "gpt-4o"
+	GPT4o20240513           = "gpt-4o-2024-05-13"
+	GPT4o20240806           = "gpt-4o-2024-08-06"
+	GPT4o20241120           = "gpt-4o-2024-11-20"
+	GPT4oLatest             = "chatgpt-4o-latest"
+	GPT4oMini               = "gpt-4o-mini"
+	GPT4oMini20240718       = "gpt-4o-mini-2024-07-18"
+	GPT4Turbo               = "gpt-4-turbo"
+	GPT4Turbo20240409       = "gpt-4-turbo-2024-04-09"
+	GPT4Turbo0125           = "gpt-4-0125-preview"
+	GPT4Turbo1106           = "gpt-4-1106-preview"
+	GPT4TurboPreview        = "gpt-4-turbo-preview"
+	GPT4VisionPreview       = "gpt-4-vision-preview"
+	GPT4                    = "gpt-4"
+	GPT4Dot1                = "gpt-4.1"
+	GPT4Dot120250414        = "gpt-4.1-2025-04-14"
+	GPT4Dot1Mini            = "gpt-4.1-mini"
+	GPT4Dot1Mini20250414    = "gpt-4.1-mini-2025-04-14"
+	GPT4Dot1Nano            = "gpt-4.1-nano"
+	GPT4Dot1Nano20250414    = "gpt-4.1-nano-2025-04-14"
+	GPT4Dot5Preview         = "gpt-4.5-preview"
+	GPT4Dot5Preview20250227 = "gpt-4.5-preview-2025-02-27"
+	GPT3Dot5Turbo0125       = "gpt-3.5-turbo-0125"
+	GPT3Dot5Turbo1106       = "gpt-3.5-turbo-1106"
+	GPT3Dot5Turbo0613       = "gpt-3.5-turbo-0613"
+	GPT3Dot5Turbo0301       = "gpt-3.5-turbo-0301"
+	GPT3Dot5Turbo16K        = "gpt-3.5-turbo-16k"
+	GPT3Dot5Turbo16K0613    = "gpt-3.5-turbo-16k-0613"
+	GPT3Dot5Turbo           = "gpt-3.5-turbo"
+	GPT3Dot5TurboInstruct   = "gpt-3.5-turbo-instruct"
 	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
 	GPT3TextDavinci003 = "text-davinci-003"
 	// Deprecated: Model is shutdown. Use gpt-3.5-turbo-instruct instead.
@@ -94,47 +95,53 @@ const (
 	CodexCodeDavinci001 = "code-davinci-001"
 )
 
-// O1SeriesModels List of new Series of OpenAI models.
-// Some old api attributes not supported.
-var O1SeriesModels = map[string]struct{}{
-	O1Mini:            {},
-	O1Mini20240912:    {},
-	O1Preview:         {},
-	O1Preview20240912: {},
-}
-
 var disabledModelsForEndpoints = map[string]map[string]bool{
 	"/completions": {
-		O1Mini:               true,
-		O1Mini20240912:       true,
-		O1Preview:            true,
-		O1Preview20240912:    true,
-		GPT3Dot5Turbo:        true,
-		GPT3Dot5Turbo0301:    true,
-		GPT3Dot5Turbo0613:    true,
-		GPT3Dot5Turbo1106:    true,
-		GPT3Dot5Turbo0125:    true,
-		GPT3Dot5Turbo16K:     true,
-		GPT3Dot5Turbo16K0613: true,
-		GPT4:                 true,
-		GPT4o:                true,
-		GPT4o20240513:        true,
-		GPT4o20240806:        true,
-		GPT4o20241120:        true,
-		GPT4oLatest:          true,
-		GPT4oMini:            true,
-		GPT4oMini20240718:    true,
-		GPT4TurboPreview:     true,
-		GPT4VisionPreview:    true,
-		GPT4Turbo1106:        true,
-		GPT4Turbo0125:        true,
-		GPT4Turbo:            true,
-		GPT4Turbo20240409:    true,
-		GPT40314:             true,
-		GPT40613:             true,
-		GPT432K:              true,
-		GPT432K0314:          true,
-		GPT432K0613:          true,
+		O1Mini:                  true,
+		O1Mini20240912:          true,
+		O1Preview:               true,
+		O1Preview20240912:       true,
+		O3Mini:                  true,
+		O3Mini20250131:          true,
+		O4Mini:                  true,
+		O4Mini20250416:          true,
+		O3:                      true,
+		O320250416:              true,
+		GPT3Dot5Turbo:           true,
+		GPT3Dot5Turbo0301:       true,
+		GPT3Dot5Turbo0613:       true,
+		GPT3Dot5Turbo1106:       true,
+		GPT3Dot5Turbo0125:       true,
+		GPT3Dot5Turbo16K:        true,
+		GPT3Dot5Turbo16K0613:    true,
+		GPT4:                    true,
+		GPT4Dot5Preview:         true,
+		GPT4Dot5Preview20250227: true,
+		GPT4o:                   true,
+		GPT4o20240513:           true,
+		GPT4o20240806:           true,
+		GPT4o20241120:           true,
+		GPT4oLatest:             true,
+		GPT4oMini:               true,
+		GPT4oMini20240718:       true,
+		GPT4TurboPreview:        true,
+		GPT4VisionPreview:       true,
+		GPT4Turbo1106:           true,
+		GPT4Turbo0125:           true,
+		GPT4Turbo:               true,
+		GPT4Turbo20240409:       true,
+		GPT40314:                true,
+		GPT40613:                true,
+		GPT432K:                 true,
+		GPT432K0314:             true,
+		GPT432K0613:             true,
+		O1:                      true,
+		GPT4Dot1:                true,
+		GPT4Dot120250414:        true,
+		GPT4Dot1Mini:            true,
+		GPT4Dot1Mini20250414:    true,
+		GPT4Dot1Nano:            true,
+		GPT4Dot1Nano20250414:    true,
 	},
 	chatCompletionsSuffix: {
 		CodexCodeDavinci002:     true,
@@ -181,64 +188,6 @@ func checkPromptType(prompt any) bool {
 	return true // all items in the slice are string, so it is []string
 }
 
-var unsupportedToolsForO1Models = map[ToolType]struct{}{
-	ToolTypeFunction: {},
-}
-
-var availableMessageRoleForO1Models = map[string]struct{}{
-	ChatMessageRoleUser:      {},
-	ChatMessageRoleAssistant: {},
-}
-
-// validateRequestForO1Models checks for deprecated fields of OpenAI models.
-func validateRequestForO1Models(request ChatCompletionRequest) error {
-	if _, found := O1SeriesModels[request.Model]; !found {
-		return nil
-	}
-
-	if request.MaxTokens > 0 {
-		return ErrO1MaxTokensDeprecated
-	}
-
-	// Logprobs: not supported.
-	if request.LogProbs {
-		return ErrO1BetaLimitationsLogprobs
-	}
-
-	// Message types: user and assistant messages only, system messages are not supported.
-	for _, m := range request.Messages {
-		if _, found := availableMessageRoleForO1Models[m.Role]; !found {
-			return ErrO1BetaLimitationsMessageTypes
-		}
-	}
-
-	// Tools: tools, function calling, and response format parameters are not supported
-	for _, t := range request.Tools {
-		if _, found := unsupportedToolsForO1Models[t.Type]; found {
-			return ErrO1BetaLimitationsTools
-		}
-	}
-
-	// Other: temperature, top_p and n are fixed at 1, while presence_penalty and frequency_penalty are fixed at 0.
-	if request.Temperature > 0 && request.Temperature != 1 {
-		return ErrO1BetaLimitationsOther
-	}
-	if request.TopP > 0 && request.TopP != 1 {
-		return ErrO1BetaLimitationsOther
-	}
-	if request.N > 0 && request.N != 1 {
-		return ErrO1BetaLimitationsOther
-	}
-	if request.PresencePenalty > 0 {
-		return ErrO1BetaLimitationsOther
-	}
-	if request.FrequencyPenalty > 0 {
-		return ErrO1BetaLimitationsOther
-	}
-
-	return nil
-}
-
 // CompletionRequest represents a request structure for completion API.
 type CompletionRequest struct {
 	Model            string  `json:"model"`
@@ -266,6 +215,8 @@ type CompletionRequest struct {
 	Temperature     float32           `json:"temperature,omitempty"`
 	TopP            float32           `json:"top_p,omitempty"`
 	User            string            `json:"user,omitempty"`
+	// Options for streaming response. Only set this when you set stream: true.
+	StreamOptions *StreamOptions `json:"stream_options,omitempty"`
 }
 
 // CompletionChoice represents one of possible completions.
@@ -291,7 +242,7 @@ type CompletionResponse struct {
 	Created int64              `json:"created"`
 	Model   string             `json:"model"`
 	Choices []CompletionChoice `json:"choices"`
-	Usage   Usage              `json:"usage"`
+	Usage   *Usage             `json:"usage,omitempty"`
 
 	httpHeader
 }
diff --git a/config.go b/config.go
index 8a91835..4788ba6 100644
--- a/config.go
+++ b/config.go
@@ -11,6 +11,8 @@ const (
 
 	azureAPIPrefix         = "openai"
 	azureDeploymentsPrefix = "deployments"
+
+	AnthropicAPIVersion = "2023-06-01"
 )
 
 type APIType string
@@ -20,6 +22,7 @@ const (
 	APITypeAzure           APIType = "AZURE"
 	APITypeAzureAD         APIType = "AZURE_AD"
 	APITypeCloudflareAzure APIType = "CLOUDFLARE_AZURE"
+	APITypeAnthropic       APIType = "ANTHROPIC"
 )
 
 const AzureAPIKeyHeader = "api-key"
@@ -37,7 +40,7 @@ type ClientConfig struct {
 	BaseURL              string
 	OrgID                string
 	APIType              APIType
-	APIVersion           string // required when APIType is APITypeAzure or APITypeAzureAD
+	APIVersion           string // required when APIType is APITypeAzure or APITypeAzureAD or APITypeAnthropic
 	AssistantVersion     string
 	AzureModelMapperFunc func(model string) string // replace model to azure deployment name func
 	HTTPClient           HTTPDoer
@@ -76,6 +79,23 @@ func DefaultAzureConfig(apiKey, baseURL string) ClientConfig {
 	}
 }
 
+func DefaultAnthropicConfig(apiKey, baseURL string) ClientConfig {
+	if baseURL == "" {
+		baseURL = "https://api.anthropic.com/v1"
+	}
+	return ClientConfig{
+		authToken:  apiKey,
+		BaseURL:    baseURL,
+		OrgID:      "",
+		APIType:    APITypeAnthropic,
+		APIVersion: AnthropicAPIVersion,
+
+		HTTPClient: &http.Client{},
+
+		EmptyMessagesLimit: defaultEmptyMessagesLimit,
+	}
+}
+
 func (ClientConfig) String() string {
 	return "<OpenAI API ClientConfig>"
 }
diff --git a/embeddings.go b/embeddings.go
index 4a0e682..8593f8b 100644
--- a/embeddings.go
+++ b/embeddings.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/base64"
 	"encoding/binary"
+	"encoding/json"
 	"errors"
 	"math"
 	"net/http"
@@ -160,6 +161,9 @@ type EmbeddingRequest struct {
 	// Dimensions The number of dimensions the resulting output embeddings should have.
 	// Only supported in text-embedding-3 and later models.
 	Dimensions int `json:"dimensions,omitempty"`
+	// The ExtraBody field allows for the inclusion of arbitrary key-value pairs
+	// in the request body that may not be explicitly defined in this struct.
+	ExtraBody map[string]any `json:"extra_body,omitempty"`
 }
 
 func (r EmbeddingRequest) Convert() EmbeddingRequest {
@@ -187,6 +191,9 @@ type EmbeddingRequestStrings struct {
 	// Dimensions The number of dimensions the resulting output embeddings should have.
 	// Only supported in text-embedding-3 and later models.
 	Dimensions int `json:"dimensions,omitempty"`
+	// The ExtraBody field allows for the inclusion of arbitrary key-value pairs
+	// in the request body that may not be explicitly defined in this struct.
+	ExtraBody map[string]any `json:"extra_body,omitempty"`
 }
 
 func (r EmbeddingRequestStrings) Convert() EmbeddingRequest {
@@ -196,6 +203,7 @@ func (r EmbeddingRequestStrings) Convert() EmbeddingRequest {
 		User:           r.User,
 		EncodingFormat: r.EncodingFormat,
 		Dimensions:     r.Dimensions,
+		ExtraBody:      r.ExtraBody,
 	}
 }
 
@@ -219,6 +227,9 @@ type EmbeddingRequestTokens struct {
 	// Dimensions The number of dimensions the resulting output embeddings should have.
 	// Only supported in text-embedding-3 and later models.
 	Dimensions int `json:"dimensions,omitempty"`
+	// The ExtraBody field allows for the inclusion of arbitrary key-value pairs
+	// in the request body that may not be explicitly defined in this struct.
+	ExtraBody map[string]any `json:"extra_body,omitempty"`
 }
 
 func (r EmbeddingRequestTokens) Convert() EmbeddingRequest {
@@ -228,6 +239,7 @@ func (r EmbeddingRequestTokens) Convert() EmbeddingRequest {
 		User:           r.User,
 		EncodingFormat: r.EncodingFormat,
 		Dimensions:     r.Dimensions,
+		ExtraBody:      r.ExtraBody,
 	}
 }
 
@@ -241,11 +253,29 @@ func (c *Client) CreateEmbeddings(
 	conv EmbeddingRequestConverter,
 ) (res EmbeddingResponse, err error) {
 	baseReq := conv.Convert()
+
+	// The body map is used to dynamically construct the request payload for the embedding API.
+	// Instead of relying on a fixed struct, the body map allows for flexible inclusion of fields
+	// based on their presence, avoiding unnecessary or empty fields in the request.
+	extraBody := baseReq.ExtraBody
+	baseReq.ExtraBody = nil
+
+	// Serialize baseReq to JSON
+	jsonData, err := json.Marshal(baseReq)
+	if err != nil {
+		return
+	}
+
+	// Deserialize JSON to map[string]any
+	var body map[string]any
+	_ = json.Unmarshal(jsonData, &body)
+
 	req, err := c.newRequest(
 		ctx,
 		http.MethodPost,
 		c.fullURL("/embeddings", withModel(string(baseReq.Model))),
-		withBody(baseReq),
+		withBody(body),           // Main request body.
+		withExtraBody(extraBody), // Merge ExtraBody fields.
 	)
 	if err != nil {
 		return
diff --git a/image.go b/image.go
index 577d7db..84b9daf 100644
--- a/image.go
+++ b/image.go
@@ -3,8 +3,8 @@ package openai
 import (
 	"bytes"
 	"context"
+	"io"
 	"net/http"
-	"os"
 	"strconv"
 )
 
@@ -13,51 +13,101 @@ const (
 	CreateImageSize256x256   = "256x256"
 	CreateImageSize512x512   = "512x512"
 	CreateImageSize1024x1024 = "1024x1024"
+
 	// dall-e-3 supported only.
 	CreateImageSize1792x1024 = "1792x1024"
 	CreateImageSize1024x1792 = "1024x1792"
+
+	// gpt-image-1 supported only.
+	CreateImageSize1536x1024 = "1536x1024" // Landscape
+	CreateImageSize1024x1536 = "1024x1536" // Portrait
 )
 
 const (
-	CreateImageResponseFormatURL     = "url"
+	// dall-e-2 and dall-e-3 only.
 	CreateImageResponseFormatB64JSON = "b64_json"
+	CreateImageResponseFormatURL     = "url"
 )
 
 const (
-	CreateImageModelDallE2 = "dall-e-2"
-	CreateImageModelDallE3 = "dall-e-3"
+	CreateImageModelDallE2    = "dall-e-2"
+	CreateImageModelDallE3    = "dall-e-3"
+	CreateImageModelGptImage1 = "gpt-image-1"
 )
 
 const (
 	CreateImageQualityHD       = "hd"
 	CreateImageQualityStandard = "standard"
+
+	// gpt-image-1 only.
+	CreateImageQualityHigh   = "high"
+	CreateImageQualityMedium = "medium"
+	CreateImageQualityLow    = "low"
 )
 
 const (
+	// dall-e-3 only.
 	CreateImageStyleVivid   = "vivid"
 	CreateImageStyleNatural = "natural"
 )
 
+const (
+	// gpt-image-1 only.
+	CreateImageBackgroundTransparent = "transparent"
+	CreateImageBackgroundOpaque      = "opaque"
+)
+
+const (
+	// gpt-image-1 only.
+	CreateImageModerationLow = "low"
+)
+
+const (
+	// gpt-image-1 only.
+	CreateImageOutputFormatPNG  = "png"
+	CreateImageOutputFormatJPEG = "jpeg"
+	CreateImageOutputFormatWEBP = "webp"
+)
+
 // ImageRequest represents the request structure for the image API.
 type ImageRequest struct {
-	Prompt         string `json:"prompt,omitempty"`
-	Model          string `json:"model,omitempty"`
-	N              int    `json:"n,omitempty"`
-	Quality        string `json:"quality,omitempty"`
-	Size           string `json:"size,omitempty"`
-	Style          string `json:"style,omitempty"`
-	ResponseFormat string `json:"response_format,omitempty"`
-	User           string `json:"user,omitempty"`
+	Prompt            string `json:"prompt,omitempty"`
+	Model             string `json:"model,omitempty"`
+	N                 int    `json:"n,omitempty"`
+	Quality           string `json:"quality,omitempty"`
+	Size              string `json:"size,omitempty"`
+	Style             string `json:"style,omitempty"`
+	ResponseFormat    string `json:"response_format,omitempty"`
+	User              string `json:"user,omitempty"`
+	Background        string `json:"background,omitempty"`
+	Moderation        string `json:"moderation,omitempty"`
+	OutputCompression int    `json:"output_compression,omitempty"`
+	OutputFormat      string `json:"output_format,omitempty"`
 }
 
 // ImageResponse represents a response structure for image API.
 type ImageResponse struct {
 	Created int64                    `json:"created,omitempty"`
 	Data    []ImageResponseDataInner `json:"data,omitempty"`
+	Usage   ImageResponseUsage       `json:"usage,omitempty"`
 
 	httpHeader
 }
 
+// ImageResponseInputTokensDetails represents the token breakdown for input tokens.
+type ImageResponseInputTokensDetails struct {
+	TextTokens  int `json:"text_tokens,omitempty"`
+	ImageTokens int `json:"image_tokens,omitempty"`
+}
+
+// ImageResponseUsage represents the token usage information for image API.
+type ImageResponseUsage struct {
+	TotalTokens        int                             `json:"total_tokens,omitempty"`
+	InputTokens        int                             `json:"input_tokens,omitempty"`
+	OutputTokens       int                             `json:"output_tokens,omitempty"`
+	InputTokensDetails ImageResponseInputTokensDetails `json:"input_tokens_details,omitempty"`
+}
+
 // ImageResponseDataInner represents a response data structure for image API.
 type ImageResponseDataInner struct {
 	URL           string `json:"url,omitempty"`
@@ -82,15 +132,42 @@ func (c *Client) CreateImage(ctx context.Context, request ImageRequest) (respons
 	return
 }
 
+// WrapReader wraps an io.Reader with filename and Content-type.
+func WrapReader(rdr io.Reader, filename string, contentType string) io.Reader {
+	return file{rdr, filename, contentType}
+}
+
+type file struct {
+	io.Reader
+	name        string
+	contentType string
+}
+
+func (f file) Name() string {
+	if f.name != "" {
+		return f.name
+	} else if named, ok := f.Reader.(interface{ Name() string }); ok {
+		return named.Name()
+	}
+	return ""
+}
+
+func (f file) ContentType() string {
+	return f.contentType
+}
+
 // ImageEditRequest represents the request structure for the image API.
+// Use WrapReader to wrap an io.Reader with filename and Content-type.
 type ImageEditRequest struct {
-	Image          *os.File `json:"image,omitempty"`
-	Mask           *os.File `json:"mask,omitempty"`
-	Prompt         string   `json:"prompt,omitempty"`
-	Model          string   `json:"model,omitempty"`
-	N              int      `json:"n,omitempty"`
-	Size           string   `json:"size,omitempty"`
-	ResponseFormat string   `json:"response_format,omitempty"`
+	Image          io.Reader `json:"image,omitempty"`
+	Mask           io.Reader `json:"mask,omitempty"`
+	Prompt         string    `json:"prompt,omitempty"`
+	Model          string    `json:"model,omitempty"`
+	N              int       `json:"n,omitempty"`
+	Size           string    `json:"size,omitempty"`
+	ResponseFormat string    `json:"response_format,omitempty"`
+	Quality        string    `json:"quality,omitempty"`
+	User           string    `json:"user,omitempty"`
 }
 
 // CreateEditImage - API call to create an image. This is the main endpoint of the DALL-E API.
@@ -98,15 +175,16 @@ func (c *Client) CreateEditImage(ctx context.Context, request ImageEditRequest)
 	body := &bytes.Buffer{}
 	builder := c.createFormBuilder(body)
 
-	// image
-	err = builder.CreateFormFile("image", request.Image)
+	// image, filename verification can be postponed
+	err = builder.CreateFormFileReader("image", request.Image, "")
 	if err != nil {
 		return
 	}
 
 	// mask, it is optional
 	if request.Mask != nil {
-		err = builder.CreateFormFile("mask", request.Mask)
+		// filename verification can be postponed
+		err = builder.CreateFormFileReader("mask", request.Mask, "")
 		if err != nil {
 			return
 		}
@@ -153,12 +231,14 @@ func (c *Client) CreateEditImage(ctx context.Context, request ImageEditRequest)
 }
 
 // ImageVariRequest represents the request structure for the image API.
+// Use WrapReader to wrap an io.Reader with filename and Content-type.
 type ImageVariRequest struct {
-	Image          *os.File `json:"image,omitempty"`
-	Model          string   `json:"model,omitempty"`
-	N              int      `json:"n,omitempty"`
-	Size           string   `json:"size,omitempty"`
-	ResponseFormat string   `json:"response_format,omitempty"`
+	Image          io.Reader `json:"image,omitempty"`
+	Model          string    `json:"model,omitempty"`
+	N              int       `json:"n,omitempty"`
+	Size           string    `json:"size,omitempty"`
+	ResponseFormat string    `json:"response_format,omitempty"`
+	User           string    `json:"user,omitempty"`
 }
 
 // CreateVariImage - API call to create an image variation. This is the main endpoint of the DALL-E API.
@@ -167,8 +247,8 @@ func (c *Client) CreateVariImage(ctx context.Context, request ImageVariRequest)
 	body := &bytes.Buffer{}
 	builder := c.createFormBuilder(body)
 
-	// image
-	err = builder.CreateFormFile("image", request.Image)
+	// image, filename verification can be postponed
+	err = builder.CreateFormFileReader("image", request.Image, "")
 	if err != nil {
 		return
 	}
diff --git a/internal/form_builder.go b/internal/form_builder.go
index 2224fad..a17e820 100644
--- a/internal/form_builder.go
+++ b/internal/form_builder.go
@@ -4,8 +4,10 @@ import (
 	"fmt"
 	"io"
 	"mime/multipart"
+	"net/textproto"
 	"os"
-	"path"
+	"path/filepath"
+	"strings"
 )
 
 type FormBuilder interface {
@@ -30,8 +32,50 @@ func (fb *DefaultFormBuilder) CreateFormFile(fieldname string, file *os.File) er
 	return fb.createFormFile(fieldname, file, file.Name())
 }
 
+var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
+
+func escapeQuotes(s string) string {
+	return quoteEscaper.Replace(s)
+}
+
+// CreateFormFileReader creates a form field with a file reader.
+// The filename in Content-Disposition is required.
 func (fb *DefaultFormBuilder) CreateFormFileReader(fieldname string, r io.Reader, filename string) error {
-	return fb.createFormFile(fieldname, r, path.Base(filename))
+	if filename == "" {
+		if f, ok := r.(interface{ Name() string }); ok {
+			filename = f.Name()
+		}
+	}
+	var contentType string
+	if f, ok := r.(interface{ ContentType() string }); ok {
+		contentType = f.ContentType()
+	}
+
+	h := make(textproto.MIMEHeader)
+	h.Set(
+		"Content-Disposition",
+		fmt.Sprintf(
+			`form-data; name="%s"; filename="%s"`,
+			escapeQuotes(fieldname),
+			escapeQuotes(filepath.Base(filename)),
+		),
+	)
+	// content type is optional, but it can be set
+	if contentType != "" {
+		h.Set("Content-Type", contentType)
+	}
+
+	fieldWriter, err := fb.writer.CreatePart(h)
+	if err != nil {
+		return err
+	}
+
+	_, err = io.Copy(fieldWriter, r)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 func (fb *DefaultFormBuilder) createFormFile(fieldname string, r io.Reader, filename string) error {
@@ -53,6 +97,9 @@ func (fb *DefaultFormBuilder) createFormFile(fieldname string, r io.Reader, file
 }
 
 func (fb *DefaultFormBuilder) WriteField(fieldname, value string) error {
+	if fieldname == "" {
+		return fmt.Errorf("fieldname cannot be empty")
+	}
 	return fb.writer.WriteField(fieldname, value)
 }
 
diff --git a/jsonschema/json.go b/jsonschema/json.go
index bcb253f..75e3b51 100644
--- a/jsonschema/json.go
+++ b/jsonschema/json.go
@@ -46,6 +46,13 @@ type Definition struct {
 	// additionalProperties: false
 	// additionalProperties: jsonschema.Definition{Type: jsonschema.String}
 	AdditionalProperties any `json:"additionalProperties,omitempty"`
+	// Whether the schema is nullable or not.
+	Nullable bool `json:"nullable,omitempty"`
+
+	// Ref Reference to a definition in $defs or external schema.
+	Ref string `json:"$ref,omitempty"`
+	// Defs A map of reusable schema definitions.
+	Defs map[string]Definition `json:"$defs,omitempty"`
 }
 
 func (d *Definition) MarshalJSON() ([]byte, error) {
@@ -65,10 +72,37 @@ func (d *Definition) Unmarshal(content string, v any) error {
 }
 
 func GenerateSchemaForType(v any) (*Definition, error) {
-	return reflectSchema(reflect.TypeOf(v))
+	var defs = make(map[string]Definition)
+	def, err := reflectSchema(reflect.TypeOf(v), defs)
+	if err != nil {
+		return nil, err
+	}
+	// If the schema has a root $ref, resolve it by:
+	// 1. Extracting the key from the $ref.
+	// 2. Detaching the referenced definition from $defs.
+	// 3. Checking for self-references in the detached definition.
+	//    - If a self-reference is found, restore the original $defs structure.
+	// 4. Flattening the referenced definition into the root schema.
+	// 5. Clearing the $ref field in the root schema.
+	if def.Ref != "" {
+		origRef := def.Ref
+		key := strings.TrimPrefix(origRef, "#/$defs/")
+		if root, ok := defs[key]; ok {
+			delete(defs, key)
+			root.Defs = defs
+			if containsRef(root, origRef) {
+				root.Defs = nil
+				defs[key] = root
+			}
+			*def = root
+		}
+		def.Ref = ""
+	}
+	def.Defs = defs
+	return def, nil
 }
 
-func reflectSchema(t reflect.Type) (*Definition, error) {
+func reflectSchema(t reflect.Type, defs map[string]Definition) (*Definition, error) {
 	var d Definition
 	switch t.Kind() {
 	case reflect.String:
@@ -82,21 +116,32 @@ func reflectSchema(t reflect.Type) (*Definition, error) {
 		d.Type = Boolean
 	case reflect.Slice, reflect.Array:
 		d.Type = Array
-		items, err := reflectSchema(t.Elem())
+		items, err := reflectSchema(t.Elem(), defs)
 		if err != nil {
 			return nil, err
 		}
 		d.Items = items
 	case reflect.Struct:
+		if t.Name() != "" {
+			if _, ok := defs[t.Name()]; !ok {
+				defs[t.Name()] = Definition{}
+				object, err := reflectSchemaObject(t, defs)
+				if err != nil {
+					return nil, err
+				}
+				defs[t.Name()] = *object
+			}
+			return &Definition{Ref: "#/$defs/" + t.Name()}, nil
+		}
 		d.Type = Object
 		d.AdditionalProperties = false
-		object, err := reflectSchemaObject(t)
+		object, err := reflectSchemaObject(t, defs)
 		if err != nil {
 			return nil, err
 		}
 		d = *object
 	case reflect.Ptr:
-		definition, err := reflectSchema(t.Elem())
+		definition, err := reflectSchema(t.Elem(), defs)
 		if err != nil {
 			return nil, err
 		}
@@ -110,7 +155,7 @@ func reflectSchema(t reflect.Type) (*Definition, error) {
 	return &d, nil
 }
 
-func reflectSchemaObject(t reflect.Type) (*Definition, error) {
+func reflectSchemaObject(t reflect.Type, defs map[string]Definition) (*Definition, error) {
 	var d = Definition{
 		Type:                 Object,
 		AdditionalProperties: false,
@@ -124,14 +169,17 @@ func reflectSchemaObject(t reflect.Type) (*Definition, error) {
 		}
 		jsonTag := field.Tag.Get("json")
 		var required = true
-		if jsonTag == "" {
+		switch {
+		case jsonTag == "-":
+			continue
+		case jsonTag == "":
 			jsonTag = field.Name
-		} else if strings.HasSuffix(jsonTag, ",omitempty") {
+		case strings.HasSuffix(jsonTag, ",omitempty"):
 			jsonTag = strings.TrimSuffix(jsonTag, ",omitempty")
 			required = false
 		}
 
-		item, err := reflectSchema(field.Type)
+		item, err := reflectSchema(field.Type, defs)
 		if err != nil {
 			return nil, err
 		}
@@ -139,6 +187,16 @@ func reflectSchemaObject(t reflect.Type) (*Definition, error) {
 		if description != "" {
 			item.Description = description
 		}
+		enum := field.Tag.Get("enum")
+		if enum != "" {
+			item.Enum = strings.Split(enum, ",")
+		}
+
+		if n := field.Tag.Get("nullable"); n != "" {
+			nullable, _ := strconv.ParseBool(n)
+			item.Nullable = nullable
+		}
+
 		properties[jsonTag] = *item
 
 		if s := field.Tag.Get("required"); s != "" {
@@ -152,3 +210,26 @@ func reflectSchemaObject(t reflect.Type) (*Definition, error) {
 	d.Properties = properties
 	return &d, nil
 }
+
+func containsRef(def Definition, targetRef string) bool {
+	if def.Ref == targetRef {
+		return true
+	}
+
+	for _, d := range def.Defs {
+		if containsRef(d, targetRef) {
+			return true
+		}
+	}
+
+	for _, prop := range def.Properties {
+		if containsRef(prop, targetRef) {
+			return true
+		}
+	}
+
+	if def.Items != nil && containsRef(*def.Items, targetRef) {
+		return true
+	}
+	return false
+}
diff --git a/jsonschema/validate.go b/jsonschema/validate.go
index 49f9b88..1bd2f80 100644
--- a/jsonschema/validate.go
+++ b/jsonschema/validate.go
@@ -5,26 +5,68 @@ import (
 	"errors"
 )
 
+func CollectDefs(def Definition) map[string]Definition {
+	result := make(map[string]Definition)
+	collectDefsRecursive(def, result, "#")
+	return result
+}
+
+func collectDefsRecursive(def Definition, result map[string]Definition, prefix string) {
+	for k, v := range def.Defs {
+		path := prefix + "/$defs/" + k
+		result[path] = v
+		collectDefsRecursive(v, result, path)
+	}
+	for k, sub := range def.Properties {
+		collectDefsRecursive(sub, result, prefix+"/properties/"+k)
+	}
+	if def.Items != nil {
+		collectDefsRecursive(*def.Items, result, prefix)
+	}
+}
+
 func VerifySchemaAndUnmarshal(schema Definition, content []byte, v any) error {
 	var data any
 	err := json.Unmarshal(content, &data)
 	if err != nil {
 		return err
 	}
-	if !Validate(schema, data) {
+	if !Validate(schema, data, WithDefs(CollectDefs(schema))) {
 		return errors.New("data validation failed against the provided schema")
 	}
 	return json.Unmarshal(content, &v)
 }
 
-func Validate(schema Definition, data any) bool {
+type validateArgs struct {
+	Defs map[string]Definition
+}
+
+type ValidateOption func(*validateArgs)
+
+func WithDefs(defs map[string]Definition) ValidateOption {
+	return func(option *validateArgs) {
+		option.Defs = defs
+	}
+}
+
+func Validate(schema Definition, data any, opts ...ValidateOption) bool {
+	args := validateArgs{}
+	for _, opt := range opts {
+		opt(&args)
+	}
+	if len(opts) == 0 {
+		args.Defs = CollectDefs(schema)
+	}
 	switch schema.Type {
 	case Object:
-		return validateObject(schema, data)
+		return validateObject(schema, data, args.Defs)
 	case Array:
-		return validateArray(schema, data)
+		return validateArray(schema, data, args.Defs)
 	case String:
-		_, ok := data.(string)
+		v, ok := data.(string)
+		if ok && len(schema.Enum) > 0 {
+			return contains(schema.Enum, v)
+		}
 		return ok
 	case Number: // float64 and int
 		_, ok := data.(float64)
@@ -45,11 +87,16 @@ func Validate(schema Definition, data any) bool {
 	case Null:
 		return data == nil
 	default:
+		if schema.Ref != "" && args.Defs != nil {
+			if v, ok := args.Defs[schema.Ref]; ok {
+				return Validate(v, data, WithDefs(args.Defs))
+			}
+		}
 		return false
 	}
 }
 
-func validateObject(schema Definition, data any) bool {
+func validateObject(schema Definition, data any, defs map[string]Definition) bool {
 	dataMap, ok := data.(map[string]any)
 	if !ok {
 		return false
@@ -61,7 +108,7 @@ func validateObject(schema Definition, data any) bool {
 	}
 	for key, valueSchema := range schema.Properties {
 		value, exists := dataMap[key]
-		if exists && !Validate(valueSchema, value) {
+		if exists && !Validate(valueSchema, value, WithDefs(defs)) {
 			return false
 		} else if !exists && contains(schema.Required, key) {
 			return false
@@ -70,13 +117,13 @@ func validateObject(schema Definition, data any) bool {
 	return true
 }
 
-func validateArray(schema Definition, data any) bool {
+func validateArray(schema Definition, data any, defs map[string]Definition) bool {
 	dataArray, ok := data.([]any)
 	if !ok {
 		return false
 	}
 	for _, item := range dataArray {
-		if !Validate(*schema.Items, item) {
+		if !Validate(*schema.Items, item, WithDefs(defs)) {
 			return false
 		}
 	}
diff --git a/messages.go b/messages.go
index 9023639..3852d2e 100644
--- a/messages.go
+++ b/messages.go
@@ -41,6 +41,7 @@ type MessageContent struct {
 	Type      string       `json:"type"`
 	Text      *MessageText `json:"text,omitempty"`
 	ImageFile *ImageFile   `json:"image_file,omitempty"`
+	ImageURL  *ImageURL    `json:"image_url,omitempty"`
 }
 type MessageText struct {
 	Value       string `json:"value"`
@@ -51,6 +52,11 @@ type ImageFile struct {
 	FileID string `json:"file_id"`
 }
 
+type ImageURL struct {
+	URL    string `json:"url"`
+	Detail string `json:"detail"`
+}
+
 type MessageRequest struct {
 	Role        string             `json:"role"`
 	Content     string             `json:"content"`
diff --git a/reasoning_validator.go b/reasoning_validator.go
new file mode 100644
index 0000000..2910b13
--- /dev/null
+++ b/reasoning_validator.go
@@ -0,0 +1,81 @@
+package openai
+
+import (
+	"errors"
+	"strings"
+)
+
+var (
+	// Deprecated: use ErrReasoningModelMaxTokensDeprecated instead.
+	ErrO1MaxTokensDeprecated                   = errors.New("this model is not supported MaxTokens, please use MaxCompletionTokens")                               //nolint:lll
+	ErrCompletionUnsupportedModel              = errors.New("this model is not supported with this method, please use CreateChatCompletion client method instead") //nolint:lll
+	ErrCompletionStreamNotSupported            = errors.New("streaming is not supported with this method, please use CreateCompletionStream")                      //nolint:lll
+	ErrCompletionRequestPromptTypeNotSupported = errors.New("the type of CompletionRequest.Prompt only supports string and []string")                              //nolint:lll
+)
+
+var (
+	ErrO1BetaLimitationsMessageTypes = errors.New("this model has beta-limitations, user and assistant messages only, system messages are not supported")       //nolint:lll
+	ErrO1BetaLimitationsTools        = errors.New("this model has beta-limitations, tools, function calling, and response format parameters are not supported") //nolint:lll
+	// Deprecated: use ErrReasoningModelLimitations* instead.
+	ErrO1BetaLimitationsLogprobs = errors.New("this model has beta-limitations, logprobs not supported")                                                                               //nolint:lll
+	ErrO1BetaLimitationsOther    = errors.New("this model has beta-limitations, temperature, top_p and n are fixed at 1, while presence_penalty and frequency_penalty are fixed at 0") //nolint:lll
+)
+
+var (
+	//nolint:lll
+	ErrReasoningModelMaxTokensDeprecated = errors.New("this model is not supported MaxTokens, please use MaxCompletionTokens")
+	ErrReasoningModelLimitationsLogprobs = errors.New("this model has beta-limitations, logprobs not supported")                                                                               //nolint:lll
+	ErrReasoningModelLimitationsOther    = errors.New("this model has beta-limitations, temperature, top_p and n are fixed at 1, while presence_penalty and frequency_penalty are fixed at 0") //nolint:lll
+)
+
+// ReasoningValidator handles validation for o-series model requests.
+type ReasoningValidator struct{}
+
+// NewReasoningValidator creates a new validator for o-series models.
+func NewReasoningValidator() *ReasoningValidator {
+	return &ReasoningValidator{}
+}
+
+// Validate performs all validation checks for o-series models.
+func (v *ReasoningValidator) Validate(request ChatCompletionRequest) error {
+	o1Series := strings.HasPrefix(request.Model, "o1")
+	o3Series := strings.HasPrefix(request.Model, "o3")
+	o4Series := strings.HasPrefix(request.Model, "o4")
+
+	if !o1Series && !o3Series && !o4Series {
+		return nil
+	}
+
+	if err := v.validateReasoningModelParams(request); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// validateReasoningModelParams checks reasoning model parameters.
+func (v *ReasoningValidator) validateReasoningModelParams(request ChatCompletionRequest) error {
+	if request.MaxTokens > 0 {
+		return ErrReasoningModelMaxTokensDeprecated
+	}
+	if request.LogProbs {
+		return ErrReasoningModelLimitationsLogprobs
+	}
+	if request.Temperature > 0 && request.Temperature != 1 {
+		return ErrReasoningModelLimitationsOther
+	}
+	if request.TopP > 0 && request.TopP != 1 {
+		return ErrReasoningModelLimitationsOther
+	}
+	if request.N > 0 && request.N != 1 {
+		return ErrReasoningModelLimitationsOther
+	}
+	if request.PresencePenalty > 0 {
+		return ErrReasoningModelLimitationsOther
+	}
+	if request.FrequencyPenalty > 0 {
+		return ErrReasoningModelLimitationsOther
+	}
+
+	return nil
+}
diff --git a/run.go b/run.go
index d3e755f..9c51aaf 100644
--- a/run.go
+++ b/run.go
@@ -83,12 +83,13 @@ const (
 )
 
 type RunRequest struct {
-	AssistantID            string         `json:"assistant_id"`
-	Model                  string         `json:"model,omitempty"`
-	Instructions           string         `json:"instructions,omitempty"`
-	AdditionalInstructions string         `json:"additional_instructions,omitempty"`
-	Tools                  []Tool         `json:"tools,omitempty"`
-	Metadata               map[string]any `json:"metadata,omitempty"`
+	AssistantID            string          `json:"assistant_id"`
+	Model                  string          `json:"model,omitempty"`
+	Instructions           string          `json:"instructions,omitempty"`
+	AdditionalInstructions string          `json:"additional_instructions,omitempty"`
+	AdditionalMessages     []ThreadMessage `json:"additional_messages,omitempty"`
+	Tools                  []Tool          `json:"tools,omitempty"`
+	Metadata               map[string]any  `json:"metadata,omitempty"`
 
 	// Sampling temperature between 0 and 2. Higher values like 0.8 are  more random.
 	// lower values are more focused and deterministic.
diff --git a/speech.go b/speech.go
index 20b52e3..60e7694 100644
--- a/speech.go
+++ b/speech.go
@@ -8,20 +8,25 @@ import (
 type SpeechModel string
 
 const (
-	TTSModel1      SpeechModel = "tts-1"
-	TTSModel1HD    SpeechModel = "tts-1-hd"
-	TTSModelCanary SpeechModel = "canary-tts"
+	TTSModel1         SpeechModel = "tts-1"
+	TTSModel1HD       SpeechModel = "tts-1-hd"
+	TTSModelCanary    SpeechModel = "canary-tts"
+	TTSModelGPT4oMini SpeechModel = "gpt-4o-mini-tts"
 )
 
 type SpeechVoice string
 
 const (
 	VoiceAlloy   SpeechVoice = "alloy"
+	VoiceAsh     SpeechVoice = "ash"
+	VoiceBallad  SpeechVoice = "ballad"
+	VoiceCoral   SpeechVoice = "coral"
 	VoiceEcho    SpeechVoice = "echo"
 	VoiceFable   SpeechVoice = "fable"
 	VoiceOnyx    SpeechVoice = "onyx"
 	VoiceNova    SpeechVoice = "nova"
 	VoiceShimmer SpeechVoice = "shimmer"
+	VoiceVerse   SpeechVoice = "verse"
 )
 
 type SpeechResponseFormat string
@@ -39,6 +44,7 @@ type CreateSpeechRequest struct {
 	Model          SpeechModel          `json:"model"`
 	Input          string               `json:"input"`
 	Voice          SpeechVoice          `json:"voice"`
+	Instructions   string               `json:"instructions,omitempty"`    // Optional, Doesnt work with tts-1 or tts-1-hd.
 	ResponseFormat SpeechResponseFormat `json:"response_format,omitempty"` // Optional, default to mp3
 	Speed          float64              `json:"speed,omitempty"`           // Optional, default to 1.0
 }
diff --git a/stream_reader.go b/stream_reader.go
index ecfa268..6faefe0 100644
--- a/stream_reader.go
+++ b/stream_reader.go
@@ -6,13 +6,14 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"regexp"
 
 	utils "github.com/sashabaranov/go-openai/internal"
 )
 
 var (
-	headerData  = []byte("data: ")
-	errorPrefix = []byte(`data: {"error":`)
+	headerData  = regexp.MustCompile(`^data:\s*`)
+	errorPrefix = regexp.MustCompile(`^data:\s*{"error":`)
 )
 
 type streamable interface {
@@ -70,12 +71,12 @@ func (stream *streamReader[T]) processLines() ([]byte, error) {
 		}
 
 		noSpaceLine := bytes.TrimSpace(rawLine)
-		if bytes.HasPrefix(noSpaceLine, errorPrefix) {
+		if errorPrefix.Match(noSpaceLine) {
 			hasErrorPrefix = true
 		}
-		if !bytes.HasPrefix(noSpaceLine, headerData) || hasErrorPrefix {
+		if !headerData.Match(noSpaceLine) || hasErrorPrefix {
 			if hasErrorPrefix {
-				noSpaceLine = bytes.TrimPrefix(noSpaceLine, headerData)
+				noSpaceLine = headerData.ReplaceAll(noSpaceLine, nil)
 			}
 			writeErr := stream.errAccumulator.Write(noSpaceLine)
 			if writeErr != nil {
@@ -89,7 +90,7 @@ func (stream *streamReader[T]) processLines() ([]byte, error) {
 			continue
 		}
 
-		noPrefixLine := bytes.TrimPrefix(noSpaceLine, headerData)
+		noPrefixLine := headerData.ReplaceAll(noSpaceLine, nil)
 		if string(noPrefixLine) == "[DONE]" {
 			stream.isFinished = true
 			return nil, io.EOF


Test patch:
diff --git a/audio_api_test.go b/audio_api_test.go
index c245984..6c6a356 100644
--- a/audio_api_test.go
+++ b/audio_api_test.go
@@ -40,12 +40,9 @@ func TestAudio(t *testing.T) {
 
 	ctx := context.Background()
 
-	dir, cleanup := test.CreateTestDirectory(t)
-	defer cleanup()
-
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
-			path := filepath.Join(dir, "fake.mp3")
+			path := filepath.Join(t.TempDir(), "fake.mp3")
 			test.CreateTestFile(t, path)
 
 			req := openai.AudioRequest{
@@ -90,12 +87,9 @@ func TestAudioWithOptionalArgs(t *testing.T) {
 
 	ctx := context.Background()
 
-	dir, cleanup := test.CreateTestDirectory(t)
-	defer cleanup()
-
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
-			path := filepath.Join(dir, "fake.mp3")
+			path := filepath.Join(t.TempDir(), "fake.mp3")
 			test.CreateTestFile(t, path)
 
 			req := openai.AudioRequest{
diff --git a/audio_test.go b/audio_test.go
index 235931f..51b3f46 100644
--- a/audio_test.go
+++ b/audio_test.go
@@ -2,20 +2,22 @@ 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) {
-	dir, cleanup := test.CreateTestDirectory(t)
-	defer cleanup()
-	path := filepath.Join(dir, "fake.mp3")
+	path := filepath.Join(t.TempDir(), "fake.mp3")
 	test.CreateTestFile(t, path)
 
 	req := AudioRequest{
@@ -63,9 +65,7 @@ func TestAudioWithFailingFormBuilder(t *testing.T) {
 
 func TestCreateFileField(t *testing.T) {
 	t.Run("createFileField failing file", func(t *testing.T) {
-		dir, cleanup := test.CreateTestDirectory(t)
-		defer cleanup()
-		path := filepath.Join(dir, "fake.mp3")
+		path := filepath.Join(t.TempDir(), "fake.mp3")
 		test.CreateTestFile(t, path)
 
 		req := AudioRequest{
@@ -111,3 +111,131 @@ func TestCreateFileField(t *testing.T) {
 		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)
+	}
+}
diff --git a/chat_stream_test.go b/chat_stream_test.go
index 28a9acf..eabb0f3 100644
--- a/chat_stream_test.go
+++ b/chat_stream_test.go
@@ -792,6 +792,223 @@ func compareChatResponses(r1, r2 openai.ChatCompletionStreamResponse) bool {
 	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
diff --git a/chat_test.go b/chat_test.go
index 134026c..172ce07 100644
--- a/chat_test.go
+++ b/chat_test.go
@@ -64,7 +64,7 @@ func TestO1ModelsChatCompletionsDeprecatedFields(t *testing.T) {
 				MaxTokens: 5,
 				Model:     openai.O1Preview,
 			},
-			expectedError: openai.ErrO1MaxTokensDeprecated,
+			expectedError: openai.ErrReasoningModelMaxTokensDeprecated,
 		},
 		{
 			name: "o1-mini_MaxTokens_deprecated",
@@ -72,7 +72,7 @@ func TestO1ModelsChatCompletionsDeprecatedFields(t *testing.T) {
 				MaxTokens: 5,
 				Model:     openai.O1Mini,
 			},
-			expectedError: openai.ErrO1MaxTokensDeprecated,
+			expectedError: openai.ErrReasoningModelMaxTokensDeprecated,
 		},
 	}
 
@@ -104,23 +104,27 @@ func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
 				LogProbs:            true,
 				Model:               openai.O1Preview,
 			},
-			expectedError: openai.ErrO1BetaLimitationsLogprobs,
+			expectedError: openai.ErrReasoningModelLimitationsLogprobs,
 		},
 		{
-			name: "message_type_unsupported",
+			name: "set_temperature_unsupported",
 			in: openai.ChatCompletionRequest{
 				MaxCompletionTokens: 1000,
 				Model:               openai.O1Mini,
 				Messages: []openai.ChatCompletionMessage{
 					{
-						Role: openai.ChatMessageRoleSystem,
+						Role: openai.ChatMessageRoleUser,
+					},
+					{
+						Role: openai.ChatMessageRoleAssistant,
 					},
 				},
+				Temperature: float32(2),
 			},
-			expectedError: openai.ErrO1BetaLimitationsMessageTypes,
+			expectedError: openai.ErrReasoningModelLimitationsOther,
 		},
 		{
-			name: "tool_unsupported",
+			name: "set_top_unsupported",
 			in: openai.ChatCompletionRequest{
 				MaxCompletionTokens: 1000,
 				Model:               openai.O1Mini,
@@ -132,16 +136,32 @@ func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
 						Role: openai.ChatMessageRoleAssistant,
 					},
 				},
-				Tools: []openai.Tool{
+				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{
 					{
-						Type: openai.ToolTypeFunction,
+						Role: openai.ChatMessageRoleUser,
+					},
+					{
+						Role: openai.ChatMessageRoleAssistant,
 					},
 				},
+				Temperature: float32(1),
+				TopP:        float32(1),
+				N:           2,
 			},
-			expectedError: openai.ErrO1BetaLimitationsTools,
+			expectedError: openai.ErrReasoningModelLimitationsOther,
 		},
 		{
-			name: "set_temperature_unsupported",
+			name: "set_presence_penalty_unsupported",
 			in: openai.ChatCompletionRequest{
 				MaxCompletionTokens: 1000,
 				Model:               openai.O1Mini,
@@ -153,15 +173,81 @@ func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
 						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.ErrO1BetaLimitationsOther,
+			expectedError: openai.ErrReasoningModelLimitationsOther,
 		},
 		{
 			name: "set_top_unsupported",
 			in: openai.ChatCompletionRequest{
 				MaxCompletionTokens: 1000,
-				Model:               openai.O1Mini,
+				Model:               openai.O3Mini,
 				Messages: []openai.ChatCompletionMessage{
 					{
 						Role: openai.ChatMessageRoleUser,
@@ -173,13 +259,13 @@ func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
 				Temperature: float32(1),
 				TopP:        float32(0.1),
 			},
-			expectedError: openai.ErrO1BetaLimitationsOther,
+			expectedError: openai.ErrReasoningModelLimitationsOther,
 		},
 		{
 			name: "set_n_unsupported",
 			in: openai.ChatCompletionRequest{
 				MaxCompletionTokens: 1000,
-				Model:               openai.O1Mini,
+				Model:               openai.O3Mini,
 				Messages: []openai.ChatCompletionMessage{
 					{
 						Role: openai.ChatMessageRoleUser,
@@ -192,13 +278,13 @@ func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
 				TopP:        float32(1),
 				N:           2,
 			},
-			expectedError: openai.ErrO1BetaLimitationsOther,
+			expectedError: openai.ErrReasoningModelLimitationsOther,
 		},
 		{
 			name: "set_presence_penalty_unsupported",
 			in: openai.ChatCompletionRequest{
 				MaxCompletionTokens: 1000,
-				Model:               openai.O1Mini,
+				Model:               openai.O3Mini,
 				Messages: []openai.ChatCompletionMessage{
 					{
 						Role: openai.ChatMessageRoleUser,
@@ -209,13 +295,13 @@ func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
 				},
 				PresencePenalty: float32(1),
 			},
-			expectedError: openai.ErrO1BetaLimitationsOther,
+			expectedError: openai.ErrReasoningModelLimitationsOther,
 		},
 		{
 			name: "set_frequency_penalty_unsupported",
 			in: openai.ChatCompletionRequest{
 				MaxCompletionTokens: 1000,
-				Model:               openai.O1Mini,
+				Model:               openai.O3Mini,
 				Messages: []openai.ChatCompletionMessage{
 					{
 						Role: openai.ChatMessageRoleUser,
@@ -226,7 +312,7 @@ func TestO1ModelsChatCompletionsBetaLimitations(t *testing.T) {
 				},
 				FrequencyPenalty: float32(0.1),
 			},
-			expectedError: openai.ErrO1BetaLimitationsOther,
+			expectedError: openai.ErrReasoningModelLimitationsOther,
 		},
 	}
 
@@ -308,6 +394,40 @@ func TestO1ModelChatCompletions(t *testing.T) {
 	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()
@@ -631,7 +751,7 @@ func TestMultipartChatMessageSerialization(t *testing.T) {
 		t.Fatalf("Unexpected error")
 	}
 	res = strings.ReplaceAll(string(s), " ", "")
-	if res != `{"role":"user","content":""}` {
+	if res != `{"role":"user"}` {
 		t.Fatalf("invalid message: %s", string(s))
 	}
 }
@@ -719,6 +839,68 @@ func handleChatCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
 	fmt.Fprintln(w, string(resBytes))
 }
 
+func handleDeepseekR1ChatCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
+	var err error
+	var resBytes []byte
+
+	// completions only accepts POST requests
+	if r.Method != "POST" {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+	}
+	var completionReq openai.ChatCompletionRequest
+	if completionReq, err = getChatCompletionBody(r); err != nil {
+		http.Error(w, "could not read request", http.StatusInternalServerError)
+		return
+	}
+	res := openai.ChatCompletionResponse{
+		ID:      strconv.Itoa(int(time.Now().Unix())),
+		Object:  "test-object",
+		Created: time.Now().Unix(),
+		// would be nice to validate Model during testing, but
+		// this may not be possible with how much upkeep
+		// would be required / wouldn't make much sense
+		Model: completionReq.Model,
+	}
+	// create completions
+	n := completionReq.N
+	if n == 0 {
+		n = 1
+	}
+	if completionReq.MaxCompletionTokens == 0 {
+		completionReq.MaxCompletionTokens = 1000
+	}
+	for i := 0; i < n; i++ {
+		reasoningContent := "User says hello! And I need to reply"
+		completionStr := strings.Repeat("a", completionReq.MaxCompletionTokens-numTokens(reasoningContent))
+		res.Choices = append(res.Choices, openai.ChatCompletionChoice{
+			Message: openai.ChatCompletionMessage{
+				Role:             openai.ChatMessageRoleAssistant,
+				ReasoningContent: reasoningContent,
+				Content:          completionStr,
+			},
+			Index: i,
+		})
+	}
+	inputTokens := numTokens(completionReq.Messages[0].Content) * n
+	completionTokens := completionReq.MaxTokens * n
+	res.Usage = openai.Usage{
+		PromptTokens:     inputTokens,
+		CompletionTokens: completionTokens,
+		TotalTokens:      inputTokens + completionTokens,
+	}
+	resBytes, _ = json.Marshal(res)
+	w.Header().Set(xCustomHeader, xCustomHeaderValue)
+	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))
+		}
+	}
+	fmt.Fprintln(w, string(resBytes))
+}
+
 // getChatCompletionBody Returns the body of the request to create a completion.
 func getChatCompletionBody(r *http.Request) (openai.ChatCompletionRequest, error) {
 	completion := openai.ChatCompletionRequest{}
@@ -764,3 +946,142 @@ func TestFinishReason(t *testing.T) {
 		}
 	}
 }
+
+func TestChatCompletionResponseFormatJSONSchema_UnmarshalJSON(t *testing.T) {
+	type args struct {
+		data []byte
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			"",
+			args{
+				data: []byte(`{
+      "name":   "math_response",
+      "strict": true,
+      "schema": {
+        "type": "object",
+        "properties": {
+          "steps": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "properties": {
+                "explanation": { "type": "string" },
+                "output":      { "type": "string" }
+              },
+              "required": ["explanation","output"],
+              "additionalProperties": false
+            }
+          },
+          "final_answer": { "type": "string" }
+        },
+        "required": ["steps","final_answer"],
+        "additionalProperties": false
+      }
+  }`),
+			},
+			false,
+		},
+		{
+			"",
+			args{
+				data: []byte(`{
+      "name":   "math_response",
+      "strict": true,
+      "schema": null
+  }`),
+			},
+			false,
+		},
+		{
+			"",
+			args{
+				data: []byte(`[123,456]`),
+			},
+			true,
+		},
+		{
+			"",
+			args{
+				data: []byte(`{
+      "name":   "math_response",
+      "strict": true,
+      "schema": 123456
+  }`),
+			},
+			true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var r openai.ChatCompletionResponseFormatJSONSchema
+			err := r.UnmarshalJSON(tt.args.data)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestChatCompletionRequest_UnmarshalJSON(t *testing.T) {
+	type args struct {
+		bs []byte
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			"",
+			args{bs: []byte(`{
+  "model": "llama3-1b",
+  "messages": [
+    { "role": "system", "content": "You are a helpful math tutor." },
+    { "role": "user",   "content": "solve 8x + 31 = 2" }
+  ],
+  "response_format": {
+    "type": "json_schema",
+    "json_schema": {
+      "name":   "math_response",
+      "strict": true,
+      "schema": {
+        "type": "object",
+        "properties": {
+          "steps": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "properties": {
+                "explanation": { "type": "string" },
+                "output":      { "type": "string" }
+              },
+              "required": ["explanation","output"],
+              "additionalProperties": false
+            }
+          },
+          "final_answer": { "type": "string" }
+        },
+        "required": ["steps","final_answer"],
+        "additionalProperties": false
+      }
+    }
+  }
+}`)},
+			false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var m openai.ChatCompletionRequest
+			err := json.Unmarshal(tt.args.bs, &m)
+			if err != nil {
+				t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/client_test.go b/client_test.go
index 2ed82f1..3219714 100644
--- a/client_test.go
+++ b/client_test.go
@@ -39,6 +39,21 @@ func TestClient(t *testing.T) {
 	}
 }
 
+func TestSetCommonHeadersAnthropic(t *testing.T) {
+	config := DefaultAnthropicConfig("mock-token", "")
+	client := NewClientWithConfig(config)
+	req, err := http.NewRequest("GET", "http://example.com", nil)
+	if err != nil {
+		t.Fatalf("Failed to create request: %v", err)
+	}
+
+	client.setCommonHeaders(req)
+
+	if got := req.Header.Get("anthropic-version"); got != AnthropicAPIVersion {
+		t.Errorf("Expected anthropic-version header to be %q, got %q", AnthropicAPIVersion, got)
+	}
+}
+
 func TestDecodeResponse(t *testing.T) {
 	stringInput := ""
 
diff --git a/completion_test.go b/completion_test.go
index 935bbe8..f0ead0d 100644
--- a/completion_test.go
+++ b/completion_test.go
@@ -33,6 +33,42 @@ func TestCompletionsWrongModel(t *testing.T) {
 	}
 }
 
+// TestCompletionsWrongModelO3 Tests the completions endpoint with O3 model which is not supported.
+func TestCompletionsWrongModelO3(t *testing.T) {
+	config := openai.DefaultConfig("whatever")
+	config.BaseURL = "http://localhost/v1"
+	client := openai.NewClientWithConfig(config)
+
+	_, err := client.CreateCompletion(
+		context.Background(),
+		openai.CompletionRequest{
+			MaxTokens: 5,
+			Model:     openai.O3,
+		},
+	)
+	if !errors.Is(err, openai.ErrCompletionUnsupportedModel) {
+		t.Fatalf("CreateCompletion should return ErrCompletionUnsupportedModel for O3, but returned: %v", err)
+	}
+}
+
+// TestCompletionsWrongModelO4Mini Tests the completions endpoint with O4Mini model which is not supported.
+func TestCompletionsWrongModelO4Mini(t *testing.T) {
+	config := openai.DefaultConfig("whatever")
+	config.BaseURL = "http://localhost/v1"
+	client := openai.NewClientWithConfig(config)
+
+	_, err := client.CreateCompletion(
+		context.Background(),
+		openai.CompletionRequest{
+			MaxTokens: 5,
+			Model:     openai.O4Mini,
+		},
+	)
+	if !errors.Is(err, openai.ErrCompletionUnsupportedModel) {
+		t.Fatalf("CreateCompletion should return ErrCompletionUnsupportedModel for O4Mini, but returned: %v", err)
+	}
+}
+
 func TestCompletionWithStream(t *testing.T) {
 	config := openai.DefaultConfig("whatever")
 	client := openai.NewClientWithConfig(config)
@@ -156,7 +192,7 @@ func handleCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
 	}
 	inputTokens *= n
 	completionTokens := completionReq.MaxTokens * len(prompts) * n
-	res.Usage = openai.Usage{
+	res.Usage = &openai.Usage{
 		PromptTokens:     inputTokens,
 		CompletionTokens: completionTokens,
 		TotalTokens:      inputTokens + completionTokens,
@@ -181,3 +217,86 @@ func getCompletionBody(r *http.Request) (openai.CompletionRequest, error) {
 	}
 	return completion, nil
 }
+
+// TestCompletionWithO1Model Tests that O1 model is not supported for completion endpoint.
+func TestCompletionWithO1Model(t *testing.T) {
+	config := openai.DefaultConfig("whatever")
+	config.BaseURL = "http://localhost/v1"
+	client := openai.NewClientWithConfig(config)
+
+	_, err := client.CreateCompletion(
+		context.Background(),
+		openai.CompletionRequest{
+			MaxTokens: 5,
+			Model:     openai.O1,
+		},
+	)
+	if !errors.Is(err, openai.ErrCompletionUnsupportedModel) {
+		t.Fatalf("CreateCompletion should return ErrCompletionUnsupportedModel for O1 model, but returned: %v", err)
+	}
+}
+
+// TestCompletionWithGPT4DotModels Tests that newer GPT4 models are not supported for completion endpoint.
+func TestCompletionWithGPT4DotModels(t *testing.T) {
+	config := openai.DefaultConfig("whatever")
+	config.BaseURL = "http://localhost/v1"
+	client := openai.NewClientWithConfig(config)
+
+	models := []string{
+		openai.GPT4Dot1,
+		openai.GPT4Dot120250414,
+		openai.GPT4Dot1Mini,
+		openai.GPT4Dot1Mini20250414,
+		openai.GPT4Dot1Nano,
+		openai.GPT4Dot1Nano20250414,
+		openai.GPT4Dot5Preview,
+		openai.GPT4Dot5Preview20250227,
+	}
+
+	for _, model := range models {
+		t.Run(model, func(t *testing.T) {
+			_, err := client.CreateCompletion(
+				context.Background(),
+				openai.CompletionRequest{
+					MaxTokens: 5,
+					Model:     model,
+				},
+			)
+			if !errors.Is(err, openai.ErrCompletionUnsupportedModel) {
+				t.Fatalf("CreateCompletion should return ErrCompletionUnsupportedModel for %s model, but returned: %v", model, err)
+			}
+		})
+	}
+}
+
+// TestCompletionWithGPT4oModels Tests that GPT4o models are not supported for completion endpoint.
+func TestCompletionWithGPT4oModels(t *testing.T) {
+	config := openai.DefaultConfig("whatever")
+	config.BaseURL = "http://localhost/v1"
+	client := openai.NewClientWithConfig(config)
+
+	models := []string{
+		openai.GPT4o,
+		openai.GPT4o20240513,
+		openai.GPT4o20240806,
+		openai.GPT4o20241120,
+		openai.GPT4oLatest,
+		openai.GPT4oMini,
+		openai.GPT4oMini20240718,
+	}
+
+	for _, model := range models {
+		t.Run(model, func(t *testing.T) {
+			_, err := client.CreateCompletion(
+				context.Background(),
+				openai.CompletionRequest{
+					MaxTokens: 5,
+					Model:     model,
+				},
+			)
+			if !errors.Is(err, openai.ErrCompletionUnsupportedModel) {
+				t.Fatalf("CreateCompletion should return ErrCompletionUnsupportedModel for %s model, but returned: %v", model, err)
+			}
+		})
+	}
+}
diff --git a/config_test.go b/config_test.go
index 3e528c3..9602308 100644
--- a/config_test.go
+++ b/config_test.go
@@ -60,3 +60,64 @@ func TestGetAzureDeploymentByModel(t *testing.T) {
 		})
 	}
 }
+
+func TestDefaultAnthropicConfig(t *testing.T) {
+	apiKey := "test-key"
+	baseURL := "https://api.anthropic.com/v1"
+
+	config := openai.DefaultAnthropicConfig(apiKey, baseURL)
+
+	if config.APIType != openai.APITypeAnthropic {
+		t.Errorf("Expected APIType to be %v, got %v", openai.APITypeAnthropic, config.APIType)
+	}
+
+	if config.APIVersion != openai.AnthropicAPIVersion {
+		t.Errorf("Expected APIVersion to be 2023-06-01, got %v", config.APIVersion)
+	}
+
+	if config.BaseURL != baseURL {
+		t.Errorf("Expected BaseURL to be %v, got %v", baseURL, config.BaseURL)
+	}
+
+	if config.EmptyMessagesLimit != 300 {
+		t.Errorf("Expected EmptyMessagesLimit to be 300, got %v", config.EmptyMessagesLimit)
+	}
+}
+
+func TestDefaultAnthropicConfigWithEmptyValues(t *testing.T) {
+	config := openai.DefaultAnthropicConfig("", "")
+
+	if config.APIType != openai.APITypeAnthropic {
+		t.Errorf("Expected APIType to be %v, got %v", openai.APITypeAnthropic, config.APIType)
+	}
+
+	if config.APIVersion != openai.AnthropicAPIVersion {
+		t.Errorf("Expected APIVersion to be %s, got %v", openai.AnthropicAPIVersion, config.APIVersion)
+	}
+
+	expectedBaseURL := "https://api.anthropic.com/v1"
+	if config.BaseURL != expectedBaseURL {
+		t.Errorf("Expected BaseURL to be %v, got %v", expectedBaseURL, config.BaseURL)
+	}
+}
+
+func TestClientConfigString(t *testing.T) {
+	// String() should always return the constant value
+	conf := openai.DefaultConfig("dummy-token")
+	expected := "<OpenAI API ClientConfig>"
+	got := conf.String()
+	if got != expected {
+		t.Errorf("ClientConfig.String() = %q; want %q", got, expected)
+	}
+}
+
+func TestGetAzureDeploymentByModel_NoMapper(t *testing.T) {
+	// On a zero-value or DefaultConfig, AzureModelMapperFunc is nil,
+	// so GetAzureDeploymentByModel should just return the input model.
+	conf := openai.DefaultConfig("dummy-token")
+	model := "some-model"
+	got := conf.GetAzureDeploymentByModel(model)
+	if got != model {
+		t.Errorf("GetAzureDeploymentByModel(%q) = %q; want %q", model, got, model)
+	}
+}
diff --git a/embeddings_test.go b/embeddings_test.go
index 4389781..07f1262 100644
--- a/embeddings_test.go
+++ b/embeddings_test.go
@@ -51,6 +51,24 @@ func TestEmbedding(t *testing.T) {
 			t.Fatalf("Expected embedding request to contain model field")
 		}
 
+		// test embedding request with strings and extra_body param
+		embeddingReqWithExtraBody := openai.EmbeddingRequest{
+			Input: []string{
+				"The food was delicious and the waiter",
+				"Other examples of embedding request",
+			},
+			Model: model,
+			ExtraBody: map[string]any{
+				"input_type": "query",
+				"truncate":   "NONE",
+			},
+		}
+		marshaled, err = json.Marshal(embeddingReqWithExtraBody)
+		checks.NoError(t, err, "Could not marshal embedding request")
+		if !bytes.Contains(marshaled, []byte(`"model":"`+model+`"`)) {
+			t.Fatalf("Expected embedding request to contain model field")
+		}
+
 		// test embedding request with strings
 		embeddingReqStrings := openai.EmbeddingRequestStrings{
 			Input: []string{
@@ -124,7 +142,33 @@ func TestEmbeddingEndpoint(t *testing.T) {
 		t.Errorf("Expected %#v embeddings, got %#v", sampleEmbeddings, res.Data)
 	}
 
-	// test create embeddings with strings (simple embedding request)
+	// test create embeddings with strings (ExtraBody in request)
+	res, err = client.CreateEmbeddings(
+		context.Background(),
+		openai.EmbeddingRequest{
+			ExtraBody: map[string]any{
+				"input_type": "query",
+				"truncate":   "NONE",
+			},
+			Dimensions: 1,
+		},
+	)
+	checks.NoError(t, err, "CreateEmbeddings error")
+	if !reflect.DeepEqual(res.Data, sampleEmbeddings) {
+		t.Errorf("Expected %#v embeddings, got %#v", sampleEmbeddings, res.Data)
+	}
+
+	// test create embeddings with strings (ExtraBody in request and )
+	_, err = client.CreateEmbeddings(
+		context.Background(),
+		openai.EmbeddingRequest{
+			Input: make(chan int), // Channels are not serializable
+			Model: "example_model",
+		},
+	)
+	checks.HasError(t, err, "CreateEmbeddings error")
+
+	// test failed (Serialize JSON error)
 	res, err = client.CreateEmbeddings(
 		context.Background(),
 		openai.EmbeddingRequest{
diff --git a/files_test.go b/files_test.go
index 3c1b99f..486ef89 100644
--- a/files_test.go
+++ b/files_test.go
@@ -121,3 +121,20 @@ func TestFileUploadWithNonExistentPath(t *testing.T) {
 	_, err := client.CreateFile(ctx, req)
 	checks.ErrorIs(t, err, os.ErrNotExist, "CreateFile should return error if file does not exist")
 }
+func TestCreateFileRequestBuilderFailure(t *testing.T) {
+	config := DefaultConfig("")
+	config.BaseURL = ""
+	client := NewClientWithConfig(config)
+	client.requestBuilder = &failingRequestBuilder{}
+
+	client.createFormBuilder = func(io.Writer) utils.FormBuilder {
+		return &mockFormBuilder{
+			mockWriteField:     func(string, string) error { return nil },
+			mockCreateFormFile: func(string, *os.File) error { return nil },
+			mockClose:          func() error { return nil },
+		}
+	}
+
+	_, err := client.CreateFile(context.Background(), FileRequest{FilePath: "client.go"})
+	checks.ErrorIs(t, err, errTestRequestBuilderFailed, "CreateFile should return error if request builder fails")
+}
diff --git a/image_api_test.go b/image_api_test.go
index 48416b1..f6057b7 100644
--- a/image_api_test.go
+++ b/image_api_test.go
@@ -7,6 +7,7 @@ import (
 	"io"
 	"net/http"
 	"os"
+	"path/filepath"
 	"testing"
 	"time"
 
@@ -86,24 +87,17 @@ func TestImageEdit(t *testing.T) {
 	defer teardown()
 	server.RegisterHandler("/v1/images/edits", handleEditImageEndpoint)
 
-	origin, err := os.Create("image.png")
+	origin, err := os.Create(filepath.Join(t.TempDir(), "image.png"))
 	if err != nil {
-		t.Error("open origin file error")
-		return
+		t.Fatalf("open origin file error: %v", err)
 	}
+	defer origin.Close()
 
-	mask, err := os.Create("mask.png")
+	mask, err := os.Create(filepath.Join(t.TempDir(), "mask.png"))
 	if err != nil {
-		t.Error("open mask file error")
-		return
+		t.Fatalf("open mask file error: %v", err)
 	}
-
-	defer func() {
-		mask.Close()
-		origin.Close()
-		os.Remove("mask.png")
-		os.Remove("image.png")
-	}()
+	defer mask.Close()
 
 	_, err = client.CreateEditImage(context.Background(), openai.ImageEditRequest{
 		Image:          origin,
@@ -121,16 +115,11 @@ func TestImageEditWithoutMask(t *testing.T) {
 	defer teardown()
 	server.RegisterHandler("/v1/images/edits", handleEditImageEndpoint)
 
-	origin, err := os.Create("image.png")
+	origin, err := os.Create(filepath.Join(t.TempDir(), "image.png"))
 	if err != nil {
-		t.Error("open origin file error")
-		return
+		t.Fatalf("open origin file error: %v", err)
 	}
-
-	defer func() {
-		origin.Close()
-		os.Remove("image.png")
-	}()
+	defer origin.Close()
 
 	_, err = client.CreateEditImage(context.Background(), openai.ImageEditRequest{
 		Image:          origin,
@@ -178,16 +167,11 @@ func TestImageVariation(t *testing.T) {
 	defer teardown()
 	server.RegisterHandler("/v1/images/variations", handleVariateImageEndpoint)
 
-	origin, err := os.Create("image.png")
+	origin, err := os.Create(filepath.Join(t.TempDir(), "image.png"))
 	if err != nil {
-		t.Error("open origin file error")
-		return
+		t.Fatalf("open origin file error: %v", err)
 	}
-
-	defer func() {
-		origin.Close()
-		os.Remove("image.png")
-	}()
+	defer origin.Close()
 
 	_, err = client.CreateVariImage(context.Background(), openai.ImageVariRequest{
 		Image:          origin,
diff --git a/image_test.go b/image_test.go
index 9332dd5..c2c8f42 100644
--- a/image_test.go
+++ b/image_test.go
@@ -4,6 +4,7 @@ import (
 	utils "github.com/sashabaranov/go-openai/internal"
 	"github.com/sashabaranov/go-openai/internal/test/checks"
 
+	"bytes"
 	"context"
 	"fmt"
 	"io"
@@ -39,120 +40,284 @@ func (fb *mockFormBuilder) FormDataContentType() string {
 }
 
 func TestImageFormBuilderFailures(t *testing.T) {
-	config := DefaultConfig("")
-	config.BaseURL = ""
-	client := NewClientWithConfig(config)
-
-	mockBuilder := &mockFormBuilder{}
-	client.createFormBuilder = func(io.Writer) utils.FormBuilder {
-		return mockBuilder
-	}
 	ctx := context.Background()
-
-	req := ImageEditRequest{
-		Mask: &os.File{},
-	}
-
 	mockFailedErr := fmt.Errorf("mock form builder fail")
-	mockBuilder.mockCreateFormFile = func(string, *os.File) error {
-		return mockFailedErr
-	}
-	_, err := client.CreateEditImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
 
-	mockBuilder.mockCreateFormFile = func(name string, _ *os.File) error {
-		if name == "mask" {
-			return mockFailedErr
-		}
-		return nil
+	newClient := func(fb *mockFormBuilder) *Client {
+		cfg := DefaultConfig("")
+		cfg.BaseURL = ""
+		c := NewClientWithConfig(cfg)
+		c.createFormBuilder = func(io.Writer) utils.FormBuilder { return fb }
+		return c
 	}
-	_, err = client.CreateEditImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
 
-	mockBuilder.mockCreateFormFile = func(string, *os.File) error {
-		return nil
+	tests := []struct {
+		name  string
+		setup func(*mockFormBuilder)
+		req   ImageEditRequest
+	}{
+		{
+			name: "image",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return mockFailedErr }
+				fb.mockWriteField = func(string, string) error { return nil }
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "mask",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(name string, _ io.Reader, _ string) error {
+					if name == "mask" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockWriteField = func(string, string) error { return nil }
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "prompt",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(field, _ string) error {
+					if field == "prompt" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "n",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(field, _ string) error {
+					if field == "n" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "size",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(field, _ string) error {
+					if field == "size" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "response_format",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(field, _ string) error {
+					if field == "response_format" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "close",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(string, string) error { return nil }
+				fb.mockClose = func() error { return mockFailedErr }
+			},
+			req: ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)},
+		},
 	}
 
-	var failForField string
-	mockBuilder.mockWriteField = func(fieldname, _ string) error {
-		if fieldname == failForField {
-			return mockFailedErr
-		}
-		return nil
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			fb := &mockFormBuilder{}
+			tc.setup(fb)
+			client := newClient(fb)
+			_, err := client.CreateEditImage(ctx, tc.req)
+			checks.ErrorIs(t, err, mockFailedErr, "CreateEditImage should return error if form builder fails")
+		})
 	}
 
-	failForField = "prompt"
-	_, err = client.CreateEditImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
-
-	failForField = "n"
-	_, err = client.CreateEditImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
-
-	failForField = "size"
-	_, err = client.CreateEditImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
-
-	failForField = "response_format"
-	_, err = client.CreateEditImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
+	t.Run("new request", func(t *testing.T) {
+		fb := &mockFormBuilder{
+			mockCreateFormFileReader: func(string, io.Reader, string) error { return nil },
+			mockWriteField:           func(string, string) error { return nil },
+			mockClose:                func() error { return nil },
+		}
+		client := newClient(fb)
+		client.requestBuilder = &failingRequestBuilder{}
 
-	failForField = ""
-	mockBuilder.mockClose = func() error {
-		return mockFailedErr
-	}
-	_, err = client.CreateEditImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
+		_, err := client.CreateEditImage(ctx, ImageEditRequest{Image: bytes.NewBuffer(nil), Mask: bytes.NewBuffer(nil)})
+		checks.ErrorIs(t, err, errTestRequestBuilderFailed, "CreateEditImage should return error if request builder fails")
+	})
 }
 
 func TestVariImageFormBuilderFailures(t *testing.T) {
-	config := DefaultConfig("")
-	config.BaseURL = ""
-	client := NewClientWithConfig(config)
-
-	mockBuilder := &mockFormBuilder{}
-	client.createFormBuilder = func(io.Writer) utils.FormBuilder {
-		return mockBuilder
-	}
 	ctx := context.Background()
+	mockFailedErr := fmt.Errorf("mock form builder fail")
 
-	req := ImageVariRequest{}
+	newClient := func(fb *mockFormBuilder) *Client {
+		cfg := DefaultConfig("")
+		cfg.BaseURL = ""
+		c := NewClientWithConfig(cfg)
+		c.createFormBuilder = func(io.Writer) utils.FormBuilder { return fb }
+		return c
+	}
 
-	mockFailedErr := fmt.Errorf("mock form builder fail")
-	mockBuilder.mockCreateFormFile = func(string, *os.File) error {
-		return mockFailedErr
+	tests := []struct {
+		name  string
+		setup func(*mockFormBuilder)
+		req   ImageVariRequest
+	}{
+		{
+			name: "image",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return mockFailedErr }
+				fb.mockWriteField = func(string, string) error { return nil }
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageVariRequest{Image: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "n",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(field string, _ string) error {
+					if field == "n" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageVariRequest{Image: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "size",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(field string, _ string) error {
+					if field == "size" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageVariRequest{Image: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "response_format",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(field string, _ string) error {
+					if field == "response_format" {
+						return mockFailedErr
+					}
+					return nil
+				}
+				fb.mockClose = func() error { return nil }
+			},
+			req: ImageVariRequest{Image: bytes.NewBuffer(nil)},
+		},
+		{
+			name: "close",
+			setup: func(fb *mockFormBuilder) {
+				fb.mockCreateFormFileReader = func(string, io.Reader, string) error { return nil }
+				fb.mockWriteField = func(string, string) error { return nil }
+				fb.mockClose = func() error { return mockFailedErr }
+			},
+			req: ImageVariRequest{Image: bytes.NewBuffer(nil)},
+		},
 	}
-	_, err := client.CreateVariImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateVariImage should return error if form builder fails")
 
-	mockBuilder.mockCreateFormFile = func(string, *os.File) error {
-		return nil
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			fb := &mockFormBuilder{}
+			tc.setup(fb)
+			client := newClient(fb)
+			_, err := client.CreateVariImage(ctx, tc.req)
+			checks.ErrorIs(t, err, mockFailedErr, "CreateVariImage should return error if form builder fails")
+		})
 	}
 
-	var failForField string
-	mockBuilder.mockWriteField = func(fieldname, _ string) error {
-		if fieldname == failForField {
-			return mockFailedErr
+	t.Run("new request", func(t *testing.T) {
+		fb := &mockFormBuilder{
+			mockCreateFormFileReader: func(string, io.Reader, string) error { return nil },
+			mockWriteField:           func(string, string) error { return nil },
+			mockClose:                func() error { return nil },
 		}
-		return nil
-	}
+		client := newClient(fb)
+		client.requestBuilder = &failingRequestBuilder{}
+
+		_, err := client.CreateVariImage(ctx, ImageVariRequest{Image: bytes.NewBuffer(nil)})
+		checks.ErrorIs(t, err, errTestRequestBuilderFailed, "CreateVariImage should return error if request builder fails")
+	})
+}
+
+type testNamedReader struct{ io.Reader }
 
-	failForField = "n"
-	_, err = client.CreateVariImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateVariImage should return error if form builder fails")
+func (testNamedReader) Name() string { return "named.txt" }
 
-	failForField = "size"
-	_, err = client.CreateVariImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateVariImage should return error if form builder fails")
+func TestWrapReader(t *testing.T) {
+	r := bytes.NewBufferString("data")
+	wrapped := WrapReader(r, "file.png", "image/png")
+	f, ok := wrapped.(interface {
+		Name() string
+		ContentType() string
+	})
+	if !ok {
+		t.Fatal("wrapped reader missing Name or ContentType")
+	}
+	if f.Name() != "file.png" {
+		t.Fatalf("expected name file.png, got %s", f.Name())
+	}
+	if f.ContentType() != "image/png" {
+		t.Fatalf("expected content type image/png, got %s", f.ContentType())
+	}
 
-	failForField = "response_format"
-	_, err = client.CreateVariImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateVariImage should return error if form builder fails")
+	// test name from underlying reader
+	nr := testNamedReader{Reader: bytes.NewBufferString("d")}
+	wrapped = WrapReader(nr, "", "text/plain")
+	f, ok = wrapped.(interface {
+		Name() string
+		ContentType() string
+	})
+	if !ok {
+		t.Fatal("wrapped named reader missing Name or ContentType")
+	}
+	if f.Name() != "named.txt" {
+		t.Fatalf("expected name named.txt, got %s", f.Name())
+	}
+	if f.ContentType() != "text/plain" {
+		t.Fatalf("expected content type text/plain, got %s", f.ContentType())
+	}
 
-	failForField = ""
-	mockBuilder.mockClose = func() error {
-		return mockFailedErr
+	// no name provided
+	wrapped = WrapReader(bytes.NewBuffer(nil), "", "")
+	f2, ok := wrapped.(interface{ Name() string })
+	if !ok {
+		t.Fatal("wrapped anonymous reader missing Name")
+	}
+	if f2.Name() != "" {
+		t.Fatalf("expected empty name, got %s", f2.Name())
 	}
-	_, err = client.CreateVariImage(ctx, req)
-	checks.ErrorIs(t, err, mockFailedErr, "CreateImage should return error if form builder fails")
 }
diff --git a/internal/error_accumulator_test.go b/internal/error_accumulator_test.go
index d48f281..f6c226c 100644
--- a/internal/error_accumulator_test.go
+++ b/internal/error_accumulator_test.go
@@ -1,41 +1,39 @@
 package openai_test
 
 import (
-	"bytes"
-	"errors"
 	"testing"
 
-	utils "github.com/sashabaranov/go-openai/internal"
+	openai "github.com/sashabaranov/go-openai/internal"
 	"github.com/sashabaranov/go-openai/internal/test"
+	"github.com/sashabaranov/go-openai/internal/test/checks"
 )
 
-func TestErrorAccumulatorBytes(t *testing.T) {
-	accumulator := &utils.DefaultErrorAccumulator{
-		Buffer: &bytes.Buffer{},
+func TestDefaultErrorAccumulator_WriteMultiple(t *testing.T) {
+	ea, ok := openai.NewErrorAccumulator().(*openai.DefaultErrorAccumulator)
+	if !ok {
+		t.Fatal("type assertion to *DefaultErrorAccumulator failed")
 	}
+	checks.NoError(t, ea.Write([]byte("{\"error\": \"test1\"}")))
+	checks.NoError(t, ea.Write([]byte("{\"error\": \"test2\"}")))
 
-	errBytes := accumulator.Bytes()
-	if len(errBytes) != 0 {
-		t.Fatalf("Did not return nil with empty bytes: %s", string(errBytes))
+	expected := "{\"error\": \"test1\"}{\"error\": \"test2\"}"
+	if string(ea.Bytes()) != expected {
+		t.Fatalf("Expected %q, got %q", expected, ea.Bytes())
 	}
+}
 
-	err := accumulator.Write([]byte("{}"))
-	if err != nil {
-		t.Fatalf("%+v", err)
+func TestDefaultErrorAccumulator_EmptyBuffer(t *testing.T) {
+	ea, ok := openai.NewErrorAccumulator().(*openai.DefaultErrorAccumulator)
+	if !ok {
+		t.Fatal("type assertion to *DefaultErrorAccumulator failed")
 	}
-
-	errBytes = accumulator.Bytes()
-	if len(errBytes) == 0 {
-		t.Fatalf("Did not return error bytes when has error: %s", string(errBytes))
+	if len(ea.Bytes()) != 0 {
+		t.Fatal("Buffer should be empty initially")
 	}
 }
 
-func TestErrorByteWriteErrors(t *testing.T) {
-	accumulator := &utils.DefaultErrorAccumulator{
-		Buffer: &test.FailingErrorBuffer{},
-	}
-	err := accumulator.Write([]byte("{"))
-	if !errors.Is(err, test.ErrTestErrorAccumulatorWriteFailed) {
-		t.Fatalf("Did not return error when write failed: %v", err)
-	}
+func TestDefaultErrorAccumulator_WriteError(t *testing.T) {
+	ea := &openai.DefaultErrorAccumulator{Buffer: &test.FailingErrorBuffer{}}
+	err := ea.Write([]byte("fail"))
+	checks.ErrorIs(t, err, test.ErrTestErrorAccumulatorWriteFailed, "Write should propagate buffer errors")
 }
diff --git a/internal/form_builder_test.go b/internal/form_builder_test.go
index d3faf99..53ef11d 100644
--- a/internal/form_builder_test.go
+++ b/internal/form_builder_test.go
@@ -1,15 +1,58 @@
 package openai //nolint:testpackage // testing private field
 
 import (
-	"github.com/sashabaranov/go-openai/internal/test"
+	"errors"
+	"io"
+
 	"github.com/sashabaranov/go-openai/internal/test/checks"
 
 	"bytes"
-	"errors"
 	"os"
+	"strings"
 	"testing"
 )
 
+type mockFormBuilder struct {
+	mockCreateFormFile func(string, *os.File) error
+	mockWriteField     func(string, string) error
+	mockClose          func() error
+}
+
+func (m *mockFormBuilder) CreateFormFile(fieldname string, file *os.File) error {
+	return m.mockCreateFormFile(fieldname, file)
+}
+
+func (m *mockFormBuilder) WriteField(fieldname, value string) error {
+	return m.mockWriteField(fieldname, value)
+}
+
+func (m *mockFormBuilder) Close() error {
+	return m.mockClose()
+}
+
+func (m *mockFormBuilder) FormDataContentType() string {
+	return ""
+}
+
+func TestCloseMethod(t *testing.T) {
+	t.Run("NormalClose", func(t *testing.T) {
+		body := &bytes.Buffer{}
+		builder := NewFormBuilder(body)
+		checks.NoError(t, builder.Close(), "正常关闭应成功")
+	})
+
+	t.Run("ErrorPropagation", func(t *testing.T) {
+		errorMock := errors.New("mock close error")
+		mockBuilder := &mockFormBuilder{
+			mockClose: func() error {
+				return errorMock
+			},
+		}
+		err := mockBuilder.Close()
+		checks.ErrorIs(t, err, errorMock, "应传递关闭错误")
+	})
+}
+
 type failingWriter struct {
 }
 
@@ -20,15 +63,11 @@ func (*failingWriter) Write([]byte) (int, error) {
 }
 
 func TestFormBuilderWithFailingWriter(t *testing.T) {
-	dir, cleanup := test.CreateTestDirectory(t)
-	defer cleanup()
-
-	file, err := os.CreateTemp(dir, "")
+	file, err := os.CreateTemp(t.TempDir(), "")
 	if err != nil {
-		t.Errorf("Error creating tmp file: %v", err)
+		t.Fatalf("Error creating tmp file: %v", err)
 	}
 	defer file.Close()
-	defer os.Remove(file.Name())
 
 	builder := NewFormBuilder(&failingWriter{})
 	err = builder.CreateFormFile("file", file)
@@ -36,15 +75,11 @@ func TestFormBuilderWithFailingWriter(t *testing.T) {
 }
 
 func TestFormBuilderWithClosedFile(t *testing.T) {
-	dir, cleanup := test.CreateTestDirectory(t)
-	defer cleanup()
-
-	file, err := os.CreateTemp(dir, "")
+	file, err := os.CreateTemp(t.TempDir(), "")
 	if err != nil {
-		t.Errorf("Error creating tmp file: %v", err)
+		t.Fatalf("Error creating tmp file: %v", err)
 	}
 	file.Close()
-	defer os.Remove(file.Name())
 
 	body := &bytes.Buffer{}
 	builder := NewFormBuilder(body)
@@ -52,3 +87,104 @@ func TestFormBuilderWithClosedFile(t *testing.T) {
 	checks.HasError(t, err, "formbuilder should return error if file is closed")
 	checks.ErrorIs(t, err, os.ErrClosed, "formbuilder should return error if file is closed")
 }
+
+type failingReader struct {
+}
+
+var errMockFailingReaderError = errors.New("mock reader failed")
+
+func (*failingReader) Read([]byte) (int, error) {
+	return 0, errMockFailingReaderError
+}
+
+type readerWithNameAndContentType struct {
+	io.Reader
+}
+
+func (*readerWithNameAndContentType) Name() string {
+	return ""
+}
+
+func (*readerWithNameAndContentType) ContentType() string {
+	return "image/png"
+}
+
+func TestFormBuilderWithReader(t *testing.T) {
+	file, err := os.CreateTemp(t.TempDir(), "")
+	if err != nil {
+		t.Fatalf("Error creating tmp file: %v", err)
+	}
+	defer file.Close()
+	builder := NewFormBuilder(&failingWriter{})
+	err = builder.CreateFormFileReader("file", file, file.Name())
+	checks.ErrorIs(t, err, errMockFailingWriterError, "formbuilder should return error if writer fails")
+
+	builder = NewFormBuilder(&bytes.Buffer{})
+	reader := &failingReader{}
+	err = builder.CreateFormFileReader("file", reader, "")
+	checks.ErrorIs(t, err, errMockFailingReaderError, "formbuilder should return error if copy reader fails")
+
+	successReader := &bytes.Buffer{}
+	err = builder.CreateFormFileReader("file", successReader, "")
+	checks.NoError(t, err, "formbuilder should not return error")
+
+	rnc := &readerWithNameAndContentType{Reader: &bytes.Buffer{}}
+	err = builder.CreateFormFileReader("file", rnc, "")
+	checks.NoError(t, err, "formbuilder should not return error")
+}
+
+func TestFormDataContentType(t *testing.T) {
+	t.Run("ReturnsUnderlyingWriterContentType", func(t *testing.T) {
+		buf := &bytes.Buffer{}
+		builder := NewFormBuilder(buf)
+
+		contentType := builder.FormDataContentType()
+		if contentType == "" {
+			t.Errorf("expected non-empty content type, got empty string")
+		}
+	})
+}
+
+func TestWriteField(t *testing.T) {
+	t.Run("EmptyFieldNameShouldReturnError", func(t *testing.T) {
+		buf := &bytes.Buffer{}
+		builder := NewFormBuilder(buf)
+
+		err := builder.WriteField("", "some value")
+		checks.HasError(t, err, "fieldname is required")
+	})
+
+	t.Run("ValidFieldNameShouldSucceed", func(t *testing.T) {
+		buf := &bytes.Buffer{}
+		builder := NewFormBuilder(buf)
+
+		err := builder.WriteField("key", "value")
+		checks.NoError(t, err, "should write field without error")
+	})
+}
+
+func TestCreateFormFile(t *testing.T) {
+	buf := &bytes.Buffer{}
+	builder := NewFormBuilder(buf)
+
+	err := builder.createFormFile("file", bytes.NewBufferString("data"), "")
+	if err == nil {
+		t.Fatal("expected error for empty filename")
+	}
+
+	builder = NewFormBuilder(&failingWriter{})
+	err = builder.createFormFile("file", bytes.NewBufferString("data"), "name")
+	checks.ErrorIs(t, err, errMockFailingWriterError, "should propagate writer error")
+}
+
+func TestCreateFormFileSuccess(t *testing.T) {
+	buf := &bytes.Buffer{}
+	builder := NewFormBuilder(buf)
+
+	err := builder.createFormFile("file", bytes.NewBufferString("data"), "foo.txt")
+	checks.NoError(t, err, "createFormFile should succeed")
+
+	if !strings.Contains(buf.String(), "filename=\"foo.txt\"") {
+		t.Fatalf("expected filename header, got %q", buf.String())
+	}
+}
diff --git a/internal/marshaller_test.go b/internal/marshaller_test.go
new file mode 100644
index 0000000..70694fa
--- /dev/null
+++ b/internal/marshaller_test.go
@@ -0,0 +1,34 @@
+package openai_test
+
+import (
+	"testing"
+
+	openai "github.com/sashabaranov/go-openai/internal"
+	"github.com/sashabaranov/go-openai/internal/test/checks"
+)
+
+func TestJSONMarshaller_Normal(t *testing.T) {
+	jm := &openai.JSONMarshaller{}
+	data := map[string]string{"key": "value"}
+
+	b, err := jm.Marshal(data)
+	checks.NoError(t, err)
+	if len(b) == 0 {
+		t.Fatal("should return non-empty bytes")
+	}
+}
+
+func TestJSONMarshaller_InvalidInput(t *testing.T) {
+	jm := &openai.JSONMarshaller{}
+	_, err := jm.Marshal(make(chan int))
+	checks.HasError(t, err, "should return error for unsupported type")
+}
+
+func TestJSONMarshaller_EmptyValue(t *testing.T) {
+	jm := &openai.JSONMarshaller{}
+	b, err := jm.Marshal(nil)
+	checks.NoError(t, err)
+	if string(b) != "null" {
+		t.Fatalf("unexpected marshaled value: %s", string(b))
+	}
+}
diff --git a/internal/request_builder_test.go b/internal/request_builder_test.go
index e26022a..adccb15 100644
--- a/internal/request_builder_test.go
+++ b/internal/request_builder_test.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"context"
 	"errors"
+	"io"
 	"net/http"
 	"reflect"
 	"testing"
@@ -59,3 +60,37 @@ func TestRequestBuilderReturnsRequestWhenRequestOfArgsIsNil(t *testing.T) {
 		t.Errorf("Build() got = %v, want %v", got, want)
 	}
 }
+
+func TestRequestBuilderWithReaderBodyAndHeader(t *testing.T) {
+	b := NewRequestBuilder()
+	ctx := context.Background()
+	method := http.MethodPost
+	url := "/reader"
+	bodyContent := "hello"
+	body := bytes.NewBufferString(bodyContent)
+	header := http.Header{"X-Test": []string{"val"}}
+
+	req, err := b.Build(ctx, method, url, body, header)
+	if err != nil {
+		t.Fatalf("Build returned error: %v", err)
+	}
+
+	gotBody, err := io.ReadAll(req.Body)
+	if err != nil {
+		t.Fatalf("cannot read body: %v", err)
+	}
+	if string(gotBody) != bodyContent {
+		t.Fatalf("expected body %q, got %q", bodyContent, string(gotBody))
+	}
+	if req.Header.Get("X-Test") != "val" {
+		t.Fatalf("expected header set to val, got %q", req.Header.Get("X-Test"))
+	}
+}
+
+func TestRequestBuilderInvalidURL(t *testing.T) {
+	b := NewRequestBuilder()
+	_, err := b.Build(context.Background(), http.MethodGet, ":", nil, nil)
+	if err == nil {
+		t.Fatal("expected error for invalid URL")
+	}
+}
diff --git a/internal/test/helpers.go b/internal/test/helpers.go
index 0e63ae8..dc5fa66 100644
--- a/internal/test/helpers.go
+++ b/internal/test/helpers.go
@@ -19,16 +19,6 @@ func CreateTestFile(t *testing.T, path string) {
 	file.Close()
 }
 
-// CreateTestDirectory creates a temporary folder which will be deleted when cleanup is called.
-func CreateTestDirectory(t *testing.T) (path string, cleanup func()) {
-	t.Helper()
-
-	path, err := os.MkdirTemp(os.TempDir(), "")
-	checks.NoError(t, err)
-
-	return path, func() { os.RemoveAll(path) }
-}
-
 // TokenRoundTripper is a struct that implements the RoundTripper
 // interface, specifically to handle the authentication token by adding a token
 // to the request header. We need this because the API requires that each
diff --git a/internal/unmarshaler_test.go b/internal/unmarshaler_test.go
new file mode 100644
index 0000000..d63eac7
--- /dev/null
+++ b/internal/unmarshaler_test.go
@@ -0,0 +1,37 @@
+package openai_test
+
+import (
+	"testing"
+
+	openai "github.com/sashabaranov/go-openai/internal"
+	"github.com/sashabaranov/go-openai/internal/test/checks"
+)
+
+func TestJSONUnmarshaler_Normal(t *testing.T) {
+	jm := &openai.JSONUnmarshaler{}
+	data := []byte(`{"key":"value"}`)
+	var v map[string]string
+
+	err := jm.Unmarshal(data, &v)
+	checks.NoError(t, err)
+	if v["key"] != "value" {
+		t.Fatal("unmarshal result mismatch")
+	}
+}
+
+func TestJSONUnmarshaler_InvalidJSON(t *testing.T) {
+	jm := &openai.JSONUnmarshaler{}
+	data := []byte(`{invalid}`)
+	var v map[string]interface{}
+
+	err := jm.Unmarshal(data, &v)
+	checks.HasError(t, err, "should return error for invalid JSON")
+}
+
+func TestJSONUnmarshaler_EmptyInput(t *testing.T) {
+	jm := &openai.JSONUnmarshaler{}
+	var v interface{}
+
+	err := jm.Unmarshal(nil, &v)
+	checks.HasError(t, err, "should return error for nil input")
+}
diff --git a/jsonschema/json_additional_test.go b/jsonschema/json_additional_test.go
new file mode 100644
index 0000000..70cf374
--- /dev/null
+++ b/jsonschema/json_additional_test.go
@@ -0,0 +1,73 @@
+package jsonschema_test
+
+import (
+	"testing"
+
+	"github.com/sashabaranov/go-openai/jsonschema"
+)
+
+// Test Definition.Unmarshal, including success path, validation error,
+// JSON syntax error and type mismatch during unmarshalling.
+func TestDefinitionUnmarshal(t *testing.T) {
+	schema := jsonschema.Definition{
+		Type: jsonschema.Object,
+		Properties: map[string]jsonschema.Definition{
+			"name": {Type: jsonschema.String},
+		},
+	}
+
+	var dst struct {
+		Name string `json:"name"`
+	}
+	if err := schema.Unmarshal(`{"name":"foo"}`, &dst); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if dst.Name != "foo" {
+		t.Errorf("expected name to be foo, got %q", dst.Name)
+	}
+
+	if err := schema.Unmarshal(`{`, &dst); err == nil {
+		t.Error("expected error for malformed json")
+	}
+
+	if err := schema.Unmarshal(`{"name":1}`, &dst); err == nil {
+		t.Error("expected validation error")
+	}
+
+	numSchema := jsonschema.Definition{Type: jsonschema.Number}
+	var s string
+	if err := numSchema.Unmarshal(`123`, &s); err == nil {
+		t.Error("expected unmarshal type error")
+	}
+}
+
+// Ensure GenerateSchemaForType returns an error when encountering unsupported types.
+func TestGenerateSchemaForTypeUnsupported(t *testing.T) {
+	type Bad struct {
+		Ch chan int `json:"ch"`
+	}
+	if _, err := jsonschema.GenerateSchemaForType(Bad{}); err == nil {
+		t.Fatal("expected error for unsupported type")
+	}
+}
+
+// Validate should fail when provided data does not match the expected container types.
+func TestValidateInvalidContainers(t *testing.T) {
+	objSchema := jsonschema.Definition{Type: jsonschema.Object}
+	if jsonschema.Validate(objSchema, 1) {
+		t.Error("expected object validation to fail for non-map input")
+	}
+
+	arrSchema := jsonschema.Definition{Type: jsonschema.Array, Items: &jsonschema.Definition{Type: jsonschema.String}}
+	if jsonschema.Validate(arrSchema, 1) {
+		t.Error("expected array validation to fail for non-slice input")
+	}
+}
+
+// Validate should return false when $ref cannot be resolved.
+func TestValidateRefNotFound(t *testing.T) {
+	refSchema := jsonschema.Definition{Ref: "#/$defs/Missing"}
+	if jsonschema.Validate(refSchema, "data", jsonschema.WithDefs(map[string]jsonschema.Definition{})) {
+		t.Error("expected validation to fail when reference is missing")
+	}
+}
diff --git a/jsonschema/json_test.go b/jsonschema/json_test.go
index 7447060..34f5d88 100644
--- a/jsonschema/json_test.go
+++ b/jsonschema/json_test.go
@@ -17,7 +17,7 @@ func TestDefinition_MarshalJSON(t *testing.T) {
 		{
 			name: "Test with empty Definition",
 			def:  jsonschema.Definition{},
-			want: `{"properties":{}}`,
+			want: `{}`,
 		},
 		{
 			name: "Test with Definition properties set",
@@ -31,15 +31,14 @@ func TestDefinition_MarshalJSON(t *testing.T) {
 				},
 			},
 			want: `{
-   "type":"string",
-   "description":"A string type",
-   "properties":{
-      "name":{
-         "type":"string",
-         "properties":{}
-      }
-   }
-}`,
+				"type":"string",
+				"description":"A string type",
+				"properties":{
+						"name":{
+							"type":"string"
+						}
+				}
+			}`,
 		},
 		{
 			name: "Test with nested Definition properties",
@@ -60,23 +59,21 @@ func TestDefinition_MarshalJSON(t *testing.T) {
 				},
 			},
 			want: `{
-   "type":"object",
-   "properties":{
-      "user":{
-         "type":"object",
-         "properties":{
-            "name":{
-               "type":"string",
-               "properties":{}
-            },
-            "age":{
-               "type":"integer",
-               "properties":{}
-            }
-         }
-      }
-   }
-}`,
+				"type":"object",
+				"properties":{
+						"user":{
+							"type":"object",
+							"properties":{
+									"name":{
+										"type":"string"
+									},
+									"age":{
+										"type":"integer"
+									}
+							}
+						}
+				}
+			}`,
 		},
 		{
 			name: "Test with complex nested Definition",
@@ -108,36 +105,32 @@ func TestDefinition_MarshalJSON(t *testing.T) {
 				},
 			},
 			want: `{
-   "type":"object",
-   "properties":{
-      "user":{
-         "type":"object",
-         "properties":{
-            "name":{
-               "type":"string",
-               "properties":{}
-            },
-            "age":{
-               "type":"integer",
-               "properties":{}
-            },
-            "address":{
-               "type":"object",
-               "properties":{
-                  "city":{
-                     "type":"string",
-                     "properties":{}
-                  },
-                  "country":{
-                     "type":"string",
-                     "properties":{}
-                  }
-               }
-            }
-         }
-      }
-   }
-}`,
+				"type":"object",
+				"properties":{
+						"user":{
+							"type":"object",
+							"properties":{
+									"name":{
+										"type":"string"
+									},
+									"age":{
+										"type":"integer"
+									},
+									"address":{
+										"type":"object",
+										"properties":{
+												"city":{
+													"type":"string"
+												},
+												"country":{
+													"type":"string"
+												}
+										}
+									}
+							}
+						}
+				}
+			}`,
 		},
 		{
 			name: "Test with Array type Definition",
@@ -153,20 +146,16 @@ func TestDefinition_MarshalJSON(t *testing.T) {
 				},
 			},
 			want: `{
-   "type":"array",
-   "items":{
-      "type":"string",
-      "properties":{
-         
-      }
-   },
-   "properties":{
-      "name":{
-         "type":"string",
-         "properties":{}
-      }
-   }
-}`,
+				"type":"array",
+				"items":{
+						"type":"string"
+				},
+				"properties":{
+						"name":{
+							"type":"string"
+						}
+				}
+			}`,
 		},
 	}
 
@@ -193,6 +182,476 @@ func TestDefinition_MarshalJSON(t *testing.T) {
 	}
 }
 
+type User struct {
+	ID     int      `json:"id,omitempty"`
+	Name   string   `json:"name,omitempty"`
+	Orders []*Order `json:"orders,omitempty"`
+}
+
+type Order struct {
+	ID     int     `json:"id,omitempty"`
+	Amount float64 `json:"amount,omitempty"`
+	Buyer  *User   `json:"buyer,omitempty"`
+}
+
+func TestStructToSchema(t *testing.T) {
+	type Tweet struct {
+		Text string `json:"text"`
+	}
+
+	type Person struct {
+		Name    string   `json:"name,omitempty"`
+		Age     int      `json:"age,omitempty"`
+		Friends []Person `json:"friends,omitempty"`
+		Tweets  []Tweet  `json:"tweets,omitempty"`
+	}
+
+	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"`
+	}
+
+	tests := []struct {
+		name string
+		in   any
+		want string
+	}{
+		{
+			name: "Test with empty struct",
+			in:   struct{}{},
+			want: `{
+				"type":"object",
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with struct containing many fields",
+			in: struct {
+				Name   string  `json:"name"`
+				Age    int     `json:"age"`
+				Active bool    `json:"active"`
+				Height float64 `json:"height"`
+				Cities []struct {
+					Name  string `json:"name"`
+					State string `json:"state"`
+				} `json:"cities"`
+			}{
+				Name: "John Doe",
+				Age:  30,
+				Cities: []struct {
+					Name  string `json:"name"`
+					State string `json:"state"`
+				}{
+					{Name: "New York", State: "NY"},
+					{Name: "Los Angeles", State: "CA"},
+				},
+			},
+			want: `{
+				"type":"object",
+				"properties":{
+					"name":{
+						"type":"string"
+					},
+					"age":{
+						"type":"integer"
+					},
+					"active":{
+						"type":"boolean"
+					},
+					"height":{
+						"type":"number"
+					},
+					"cities":{
+						"type":"array",
+						"items":{
+							"additionalProperties":false,
+							"type":"object",
+							"properties":{
+								"name":{
+									"type":"string"
+								},
+								"state":{
+									"type":"string"
+								}
+							},
+							"required":["name","state"]
+						}
+					}
+				},
+				"required":["name","age","active","height","cities"],
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with description tag",
+			in: struct {
+				Name string `json:"name" description:"The name of the person"`
+			}{
+				Name: "John Doe",
+			},
+			want: `{
+				"type":"object",
+				"properties":{
+					"name":{
+						"type":"string",
+						"description":"The name of the person"
+					}
+				},
+				"required":["name"],
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with required tag",
+			in: struct {
+				Name string `json:"name" required:"false"`
+			}{
+				Name: "John Doe",
+			},
+			want: `{
+				"type":"object",
+				"properties":{
+					"name":{
+						"type":"string"
+					}
+				},
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with enum tag",
+			in: struct {
+				Color string `json:"color" enum:"red,green,blue"`
+			}{
+				Color: "red",
+			},
+			want: `{
+				"type":"object",
+				"properties":{
+					"color":{
+						"type":"string",
+						"enum":["red","green","blue"]
+					}
+				},
+				"required":["color"],
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with nullable tag",
+			in: struct {
+				Name *string `json:"name" nullable:"true"`
+			}{
+				Name: nil,
+			},
+			want: `{
+
+				"type":"object",
+				"properties":{
+					"name":{
+						"type":"string",
+						"nullable":true
+					}
+				},
+				"required":["name"],
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with exclude mark",
+			in: struct {
+				Name string `json:"-"`
+			}{
+				Name: "Name",
+			},
+			want: `{
+				"type":"object",
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with no json tag",
+			in: struct {
+				Name string
+			}{
+				Name: "",
+			},
+			want: `{
+				"type":"object",
+				"properties":{
+					"Name":{
+						"type":"string"
+					}
+				},
+				"required":["Name"],
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with omitempty tag",
+			in: struct {
+				Name string `json:"name,omitempty"`
+			}{
+				Name: "",
+			},
+			want: `{
+				"type":"object",
+				"properties":{
+					"name":{
+						"type":"string"
+					}
+				},
+				"additionalProperties":false
+			}`,
+		},
+		{
+			name: "Test with $ref and $defs",
+			in: struct {
+				Person Person  `json:"person"`
+				Tweets []Tweet `json:"tweets"`
+			}{},
+			want: `{
+  "type" : "object",
+  "properties" : {
+    "person" : {
+      "$ref" : "#/$defs/Person"
+    },
+    "tweets" : {
+      "type" : "array",
+      "items" : {
+        "$ref" : "#/$defs/Tweet"
+      }
+    }
+  },
+  "required" : [ "person", "tweets" ],
+  "additionalProperties" : false,
+  "$defs" : {
+    "Person" : {
+      "type" : "object",
+      "properties" : {
+        "age" : {
+          "type" : "integer"
+        },
+        "friends" : {
+          "type" : "array",
+          "items" : {
+            "$ref" : "#/$defs/Person"
+          }
+        },
+        "name" : {
+          "type" : "string"
+        },
+        "tweets" : {
+          "type" : "array",
+          "items" : {
+            "$ref" : "#/$defs/Tweet"
+          }
+        }
+      },
+      "additionalProperties" : false
+    },
+    "Tweet" : {
+      "type" : "object",
+      "properties" : {
+        "text" : {
+          "type" : "string"
+        }
+      },
+      "required" : [ "text" ],
+      "additionalProperties" : false
+    }
+  }
+}`,
+		},
+		{
+			name: "Test Person",
+			in:   Person{},
+			want: `{
+  "type": "object",
+  "properties": {
+    "age": {
+      "type": "integer"
+    },
+    "friends": {
+      "type": "array",
+      "items": {
+        "$ref": "#/$defs/Person"
+      }
+    },
+    "name": {
+      "type": "string"
+    },
+    "tweets": {
+      "type": "array",
+      "items": {
+        "$ref": "#/$defs/Tweet"
+      }
+    }
+  },
+  "additionalProperties": false,
+  "$defs": {
+    "Person": {
+      "type": "object",
+      "properties": {
+        "age": {
+          "type": "integer"
+        },
+        "friends": {
+          "type": "array",
+          "items": {
+            "$ref": "#/$defs/Person"
+          }
+        },
+        "name": {
+          "type": "string"
+        },
+        "tweets": {
+          "type": "array",
+          "items": {
+            "$ref": "#/$defs/Tweet"
+          }
+        }
+      },
+      "additionalProperties": false
+    },
+    "Tweet": {
+      "type": "object",
+      "properties": {
+        "text": {
+          "type": "string"
+        }
+      },
+      "required": [
+        "text"
+      ],
+      "additionalProperties": false
+    }
+  }
+}`,
+		},
+		{
+			name: "Test MyStructuredResponse",
+			in:   MyStructuredResponse{},
+			want: `{
+  "type": "object",
+  "properties": {
+    "camel_case": {
+      "type": "string",
+      "description": "CamelCase"
+    },
+    "kebab_case": {
+      "type": "string",
+      "description": "KebabCase"
+    },
+    "pascal_case": {
+      "type": "string",
+      "description": "PascalCase"
+    },
+    "snake_case": {
+      "type": "string",
+      "description": "SnakeCase"
+    }
+  },
+  "required": [
+    "pascal_case",
+    "camel_case",
+    "kebab_case",
+    "snake_case"
+  ],
+  "additionalProperties": false
+}`,
+		},
+		{
+			name: "Test User",
+			in:   User{},
+			want: `{
+  "type": "object",
+  "properties": {
+    "id": {
+      "type": "integer"
+    },
+    "name": {
+      "type": "string"
+    },
+    "orders": {
+      "type": "array",
+      "items": {
+        "$ref": "#/$defs/Order"
+      }
+    }
+  },
+  "additionalProperties": false,
+  "$defs": {
+    "Order": {
+      "type": "object",
+      "properties": {
+        "amount": {
+          "type": "number"
+        },
+        "buyer": {
+          "$ref": "#/$defs/User"
+        },
+        "id": {
+          "type": "integer"
+        }
+      },
+      "additionalProperties": false
+    },
+    "User": {
+      "type": "object",
+      "properties": {
+        "id": {
+          "type": "integer"
+        },
+        "name": {
+          "type": "string"
+        },
+        "orders": {
+          "type": "array",
+          "items": {
+            "$ref": "#/$defs/Order"
+          }
+        }
+      },
+      "additionalProperties": false
+    }
+  }
+}`,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			wantBytes := []byte(tt.want)
+
+			schema, err := jsonschema.GenerateSchemaForType(tt.in)
+			if err != nil {
+				t.Errorf("Failed to generate schema: error = %v", err)
+				return
+			}
+
+			var want map[string]interface{}
+			err = json.Unmarshal(wantBytes, &want)
+			if err != nil {
+				t.Errorf("Failed to Unmarshal JSON: error = %v", err)
+				return
+			}
+
+			got := structToMap(t, schema)
+			gotPtr := structToMap(t, &schema)
+
+			if !reflect.DeepEqual(got, want) {
+				t.Errorf("MarshalJSON() got = %v, want %v", got, want)
+			}
+			if !reflect.DeepEqual(gotPtr, want) {
+				t.Errorf("MarshalJSON() gotPtr = %v, want %v", gotPtr, want)
+			}
+		})
+	}
+}
+
 func structToMap(t *testing.T, v any) map[string]any {
 	t.Helper()
 	gotBytes, err := json.Marshal(v)
diff --git a/jsonschema/validate_test.go b/jsonschema/validate_test.go
index 6fa30ab..aefdf40 100644
--- a/jsonschema/validate_test.go
+++ b/jsonschema/validate_test.go
@@ -1,6 +1,7 @@
 package jsonschema_test
 
 import (
+	"reflect"
 	"testing"
 
 	"github.com/sashabaranov/go-openai/jsonschema"
@@ -70,6 +71,96 @@ func Test_Validate(t *testing.T) {
 		},
 			Required: []string{"string"},
 		}}, false},
+		{
+			"test schema with ref and defs", args{data: map[string]any{
+				"person": map[string]any{
+					"name":   "John",
+					"gender": "male",
+					"age":    28,
+					"profile": map[string]any{
+						"full_name": "John Doe",
+					},
+				},
+			}, schema: jsonschema.Definition{
+				Type: jsonschema.Object,
+				Properties: map[string]jsonschema.Definition{
+					"person": {Ref: "#/$defs/Person"},
+				},
+				Required: []string{"person"},
+				Defs: map[string]jsonschema.Definition{
+					"Person": {
+						Type: jsonschema.Object,
+						Properties: map[string]jsonschema.Definition{
+							"name":    {Type: jsonschema.String},
+							"gender":  {Type: jsonschema.String, Enum: []string{"male", "female", "unknown"}},
+							"age":     {Type: jsonschema.Integer},
+							"profile": {Ref: "#/$defs/Person/$defs/Profile"},
+							"tweets":  {Type: jsonschema.Array, Items: &jsonschema.Definition{Ref: "#/$defs/Tweet"}},
+						},
+						Required: []string{"name", "gender", "age", "profile"},
+						Defs: map[string]jsonschema.Definition{
+							"Profile": {
+								Type: jsonschema.Object,
+								Properties: map[string]jsonschema.Definition{
+									"full_name": {Type: jsonschema.String},
+								},
+							},
+						},
+					},
+					"Tweet": {
+						Type: jsonschema.Object,
+						Properties: map[string]jsonschema.Definition{
+							"text":   {Type: jsonschema.String},
+							"person": {Ref: "#/$defs/Person"},
+						},
+					},
+				},
+			}}, true},
+		{
+			"test enum invalid value", args{data: map[string]any{
+				"person": map[string]any{
+					"name":   "John",
+					"gender": "other",
+					"age":    28,
+					"profile": map[string]any{
+						"full_name": "John Doe",
+					},
+				},
+			}, schema: jsonschema.Definition{
+				Type: jsonschema.Object,
+				Properties: map[string]jsonschema.Definition{
+					"person": {Ref: "#/$defs/Person"},
+				},
+				Required: []string{"person"},
+				Defs: map[string]jsonschema.Definition{
+					"Person": {
+						Type: jsonschema.Object,
+						Properties: map[string]jsonschema.Definition{
+							"name":    {Type: jsonschema.String},
+							"gender":  {Type: jsonschema.String, Enum: []string{"male", "female", "unknown"}},
+							"age":     {Type: jsonschema.Integer},
+							"profile": {Ref: "#/$defs/Person/$defs/Profile"},
+							"tweets":  {Type: jsonschema.Array, Items: &jsonschema.Definition{Ref: "#/$defs/Tweet"}},
+						},
+						Required: []string{"name", "gender", "age", "profile"},
+						Defs: map[string]jsonschema.Definition{
+							"Profile": {
+								Type: jsonschema.Object,
+								Properties: map[string]jsonschema.Definition{
+									"full_name": {Type: jsonschema.String},
+								},
+							},
+						},
+					},
+					"Tweet": {
+						Type: jsonschema.Object,
+						Properties: map[string]jsonschema.Definition{
+							"text":   {Type: jsonschema.String},
+							"person": {Ref: "#/$defs/Person"},
+						},
+					},
+				},
+			}}, false},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -156,8 +247,100 @@ func TestUnmarshal(t *testing.T) {
 			err := jsonschema.VerifySchemaAndUnmarshal(tt.args.schema, tt.args.content, tt.args.v)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Unmarshal() error = %v, wantErr %v", err, tt.wantErr)
-			} else if err == nil {
-				t.Logf("Unmarshal() v = %+v\n", tt.args.v)
+			}
+		})
+	}
+}
+
+func TestCollectDefs(t *testing.T) {
+	type args struct {
+		schema jsonschema.Definition
+	}
+	tests := []struct {
+		name string
+		args args
+		want map[string]jsonschema.Definition
+	}{
+		{
+			"test collect defs",
+			args{
+				schema: jsonschema.Definition{
+					Type: jsonschema.Object,
+					Properties: map[string]jsonschema.Definition{
+						"person": {Ref: "#/$defs/Person"},
+					},
+					Required: []string{"person"},
+					Defs: map[string]jsonschema.Definition{
+						"Person": {
+							Type: jsonschema.Object,
+							Properties: map[string]jsonschema.Definition{
+								"name":    {Type: jsonschema.String},
+								"gender":  {Type: jsonschema.String, Enum: []string{"male", "female", "unknown"}},
+								"age":     {Type: jsonschema.Integer},
+								"profile": {Ref: "#/$defs/Person/$defs/Profile"},
+								"tweets":  {Type: jsonschema.Array, Items: &jsonschema.Definition{Ref: "#/$defs/Tweet"}},
+							},
+							Required: []string{"name", "gender", "age", "profile"},
+							Defs: map[string]jsonschema.Definition{
+								"Profile": {
+									Type: jsonschema.Object,
+									Properties: map[string]jsonschema.Definition{
+										"full_name": {Type: jsonschema.String},
+									},
+								},
+							},
+						},
+						"Tweet": {
+							Type: jsonschema.Object,
+							Properties: map[string]jsonschema.Definition{
+								"text":   {Type: jsonschema.String},
+								"person": {Ref: "#/$defs/Person"},
+							},
+						},
+					},
+				},
+			},
+			map[string]jsonschema.Definition{
+				"#/$defs/Person": {
+					Type: jsonschema.Object,
+					Properties: map[string]jsonschema.Definition{
+						"name":    {Type: jsonschema.String},
+						"gender":  {Type: jsonschema.String, Enum: []string{"male", "female", "unknown"}},
+						"age":     {Type: jsonschema.Integer},
+						"profile": {Ref: "#/$defs/Person/$defs/Profile"},
+						"tweets":  {Type: jsonschema.Array, Items: &jsonschema.Definition{Ref: "#/$defs/Tweet"}},
+					},
+					Required: []string{"name", "gender", "age", "profile"},
+					Defs: map[string]jsonschema.Definition{
+						"Profile": {
+							Type: jsonschema.Object,
+							Properties: map[string]jsonschema.Definition{
+								"full_name": {Type: jsonschema.String},
+							},
+						},
+					},
+				},
+				"#/$defs/Person/$defs/Profile": {
+					Type: jsonschema.Object,
+					Properties: map[string]jsonschema.Definition{
+						"full_name": {Type: jsonschema.String},
+					},
+				},
+				"#/$defs/Tweet": {
+					Type: jsonschema.Object,
+					Properties: map[string]jsonschema.Definition{
+						"text":   {Type: jsonschema.String},
+						"person": {Ref: "#/$defs/Person"},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := jsonschema.CollectDefs(tt.args.schema)
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("CollectDefs() = %v, want %v", got, tt.want)
 			}
 		})
 	}
diff --git a/models_test.go b/models_test.go
index 24a28ed..7fd010c 100644
--- a/models_test.go
+++ b/models_test.go
@@ -47,6 +47,24 @@ func TestGetModel(t *testing.T) {
 	checks.NoError(t, err, "GetModel error")
 }
 
+// TestGetModelO3 Tests the retrieve O3 model endpoint of the API using the mocked server.
+func TestGetModelO3(t *testing.T) {
+	client, server, teardown := setupOpenAITestServer()
+	defer teardown()
+	server.RegisterHandler("/v1/models/o3", handleGetModelEndpoint)
+	_, err := client.GetModel(context.Background(), "o3")
+	checks.NoError(t, err, "GetModel error for O3")
+}
+
+// TestGetModelO4Mini Tests the retrieve O4Mini model endpoint of the API using the mocked server.
+func TestGetModelO4Mini(t *testing.T) {
+	client, server, teardown := setupOpenAITestServer()
+	defer teardown()
+	server.RegisterHandler("/v1/models/o4-mini", handleGetModelEndpoint)
+	_, err := client.GetModel(context.Background(), "o4-mini")
+	checks.NoError(t, err, "GetModel error for O4Mini")
+}
+
 func TestAzureGetModel(t *testing.T) {
 	client, server, teardown := setupAzureTestServer()
 	defer teardown()
diff --git a/openai_test.go b/openai_test.go
index 729d888..a55f3a8 100644
--- a/openai_test.go
+++ b/openai_test.go
@@ -29,9 +29,9 @@ func setupAzureTestServer() (client *openai.Client, server *test.ServerTest, tea
 
 // numTokens Returns the number of GPT-3 encoded tokens in the given text.
 // This function approximates based on the rule of thumb stated by OpenAI:
-// https://beta.openai.com/tokenizer
+// https://beta.openai.com/tokenizer.
 //
-// TODO: implement an actual tokenizer for GPT-3 and Codex (once available)
+// TODO: implement an actual tokenizer for GPT-3 and Codex (once available).
 func numTokens(s string) int {
 	return int(float32(len(s)) / 4)
 }
diff --git a/speech_test.go b/speech_test.go
index f1e405c..67a3fea 100644
--- a/speech_test.go
+++ b/speech_test.go
@@ -21,10 +21,8 @@ func TestSpeechIntegration(t *testing.T) {
 	defer teardown()
 
 	server.RegisterHandler("/v1/audio/speech", func(w http.ResponseWriter, r *http.Request) {
-		dir, cleanup := test.CreateTestDirectory(t)
-		path := filepath.Join(dir, "fake.mp3")
+		path := filepath.Join(t.TempDir(), "fake.mp3")
 		test.CreateTestFile(t, path)
-		defer cleanup()
 
 		// audio endpoints only accept POST requests
 		if r.Method != "POST" {