openai-php / laravel

⚡️ OpenAI PHP for Laravel is a supercharged PHP API client that allows you to interact with OpenAI API

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add test fake

gehrisandro opened this issue · comments

Hi @nunomaduro

While using the package I ran into the problem that testing / mocking is too complicated and I tried to make it more Laravel like. So I tinkered a bit around and came up with a solution inspired by Laravels Mail::fake() and Http::fake() capabilities.

As the code is still a terrible mess I didn't submit a PR yet instead here is a test snippet which should explain how it works:

  public function test_open_ai_fake(): void
  {
      // switch to fake mode and pass the fake response
      OpenAI::fake([
          CreateResponse::from([
              'choices' => [
                  [
                      'text' => 'awesome!',
                      'index' => 0,
                      'logprobs' => null,
                      'finish_reason' => 'length',
                  ],
              ],
              // ... all the other required response params
          ]),
      ]);

      // execute a request on the api
      $response = OpenAI::completions()->create([
          'model' => 'text-davinci-003',
          'prompt' => 'PHP is ',
      ]);

      // verify the (fake) response
      $this->assertEquals('awesome!', $response['choices'][0]['text']);

      // do various asserts if the expected request was sent
      // all asserts can be done on the facade by passing the resource class ...
      OpenAI::assertSent(Completions::class, function ($method, $parameters) {
          return $method === 'create' &&
              $parameters['model'] === 'text-davinci-003' &&
              $parameters['prompt'] === 'PHP is ';
      });

      // ... or directly on the (faked) resource
      OpenAI::completions()->assertSent(function ($method, $parameters) {
          return $method === 'create' &&
              $parameters['model'] === 'text-davinci-003' &&
              $parameters['prompt'] === 'PHP is ';
      });

      OpenAI::assertSent(Completions::class);
      OpenAI::completions()->assertSent();

      OpenAI::assertSent(Completions::class, 1);
      OpenAI::completions()->assertSent(1);

      OpenAI::assertNotSent(Completions::class, function ($method, $parameters) {
          return $parameters['prompt'] === 'Python is ';
      });
      OpenAI::completions()->assertNotSent(function ($method, $parameters) {
          return $parameters['prompt'] === 'Python is ';
      });

      OpenAI::assertNotSent(Chat::class);
      OpenAI::chat()->assertNotSent();

      OpenAI::assertNothingSent(); // this one does fail because something was sent
  }

Do you think this is worth to continue?

At least in my current project it already helped me a lot 😉

Yes. You can move forward.

@gehrisandro happy to help with docs if needed

any progress on this? As a work around I did this

Made a wrapper call just for completions

<?php

namespace App\OpenAi;

use OpenAI\Laravel\Facades\OpenAI;

class CompletionsClientWrapper
{
    public function generate($prompt): string
    {
        if (config('openai.mock')) {
            $data = get_fixture('chapter_response.json');

            return data_get($data, 'choices.0.text');
        }

        $result = OpenAI::completions()->create([
            'model' => 'text-davinci-003',
            'prompt' => $prompt,
            'max_tokens' => 2000,
            'temperature' => 0,
        ]);

        $context = data_get($result, 'choices.0.text');

        return $context;
    }
}

Then the json file is in tests/fixtures

{
    "id": "cmpl-71xqlCN3VKaEP1xAB18fSrHnrXpAX",
    "object": "text_completion",
    "created": 1680702747,
    "model": "text-davinci-003",
    "choices": [
        {
            "text": "\n\nSome text here",
            "index": 0,
            "logprobs": null,
            "finishReason": "stop"
        }
    ],
    "usage": {
        "promptTokens": 57,
        "completionTokens": 269,
        "totalTokens": 326
    }
}

Then the .env I did this

OPENAI_MOCK=true

This allowed me to work on the UI without hitting the API and in my tests I did this

\Facades\App\OpenAi\CompletionsClientWrapper::shouldReceive('generate')->andReturn('Some content here');

Taking advantage of Laravel's realtime facades.

The controller uses it too like that

$context = \Facades\App\OpenAi\CompletionsClientWrapper::generate($prompt);