openai-php / client

⚡️ OpenAI PHP is a supercharged community-maintained PHP API client that allows you to interact with OpenAI API.
MIT License
4.98k stars 519 forks source link

[Bug]: failed to upload files with Azure OpenAI #465

Closed chris-lee-lb closed 2 months ago

chris-lee-lb commented 3 months ago

Description

Currently can not upload files with Azure OpenAI successfully. Will receive this error messages.

{
  "error": {
    "code": "invalidPayload",
    "message": "purpose contains an invalid purpose."
  }
}

Steps To Reproduce

<?php

$client = OpenAI::factory()
    ->withBaseUri('<azure_openai_endpoint>')
    ->withHttpHeader('api-key', '<azure_openai_api_key>')
    ->withQueryParam('api-version', '2024-05-01-preview')
    ->withHttpClient(new \GuzzleHttp\Client(['timeout' => 60]))
    ->make();

$client->files()->upload([
    'file'    => fopen('<file_path>', 'r'),
    'purpose' => 'assistants',
]);

OpenAI PHP Client Version

v0.10.1

PHP Version

8.3.10

Notes

After further root cause analysis, it was discovered that the issue was due to the Azure OpenAI API Server being unable to correctly parse the content-length in the multipart/form-data request body sent by the OpenAI HTTP client (came from this class - Http\Message\MultipartStream\MultipartStreamBuilder). Once this content-length is removed, the upload works as expected. Currently, there is a related pull request (https://github.com/guzzle/psr7/pull/581) for the GuzzleHttp Client that is awaiting merge.

chris-lee-lb commented 3 months ago

I've send this PR to php-http/multipart-stream-builder (https://github.com/php-http/multipart-stream-builder/pull/63), hope can be merged. Or we may have to look for other possible alternatives first.

mohammedhanafy commented 3 months ago

@chris-lee-lb hi chris, i'm having the same exact issue, but i tried to remove the content-length code from /vendor/guzzlehttp/psr7/src/MultipartStream.php, but i still have the same issue

$client = OpenAI::factory()
            ->withBaseUri('https://curans.openai.azure.com/openai/')
            ->withHttpHeader('api-key', $apiKey)
            ->withQueryParam('api-version', config('openai.azure.files_api_version'))
            ->withHttpClient(new \GuzzleHttp\Client(['timeout' => 60]))
            ->make();
$response = $client->files()->upload([
    'purpose' => 'assistants',
    'file' => fopen(Storage::path('public/'.$pdfFilePath), 'r'),
]);

"message": "purpose contains an invalid purpose."

is there an alternative way to fix this issue ? thank you so much

chris-lee-lb commented 2 months ago

@mohammedhanafy Because we currently create custom function with Laravel Http Client Facade and our own MultipartStream class (which based on GuzzleHttp\Psr7\MultipartStream) to workaround this issue.

use App\Support\MultipartStream;
use Illuminate\Support\Facades\Http;

$body     = new MultipartStream($elements);
$boundary = $body->getBoundary();

$response = retry(
    3,
    fn () => Http::baseUrl($endpoint)
        ->withBody($body, "multipart/form-data; boundary={$boundary}")
        ->timeout($timeout)
        ->retry(3, 100, fn ($ex) => $ex instanceof ConnectionException)
        ->withHeaders(['api-key' => $apiKey])
        ->withQueryParameters(['api-version' => $apiVersion])
        ->send('post', '/files')
        ->throwUnlessStatus(201),
    1000
);
chris-lee-lb commented 2 months ago

https://github.com/php-http/multipart-stream-builder/pull/63 has been approved & released as 1.4.0, so this issue can be closed. cc @mohammedhanafy