Skip to content

Commit

Permalink
Merge pull request #69 from outl1ne/Virtual-branch
Browse files Browse the repository at this point in the history
feat(responses): Add json and thread methods to MessagesResponse
  • Loading branch information
allantatter authored Oct 21, 2024
2 parents d3655b7 + a05c784 commit 2b7f7b3
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 22 deletions.
83 changes: 74 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,67 @@ $deletedFile = OpenAI::files()->delete($file->id);
```php
$response = OpenAI::chat()->create(
model: 'gpt-3.5-turbo',
messages: (new Messages)->system('You are a helpful assistant.')->user('Hello!'),
);
messages: Messages::make()->system('You are a helpful assistant.')->user('Hello!'),
)->json();
```

Enable JSON response formatting:

```php
$response = OpenAI::chat()->create(
model: 'gpt-3.5-turbo',
messages: (new Messages)->system('You are a helpful assistant.')->user('Suggest me tasty fruits as JSON array of fruits.'),
responseFormat: (new ResponseFormat)->json(),
);
messages: Messages::make()->system('You are a helpful assistant.')->user('Suggest me tasty fruits as JSON array of fruits.'),
responseFormat: ResponseFormat::make()->json(),
)->json();
```

JSON Structured Outputs example:

```php
$response = OpenAI::chat()->create(
model: 'gpt-4o-mini',
messages: Messages::make()->system('You are a helpful assistant.')->user('Suggest me 10 tasty fruits.'),
responseFormat: ResponseFormat::make()->jsonSchema(
JsonObject::make()
->property('fruits', JsonArray::make()->items(JsonString::make()))
->property('number_of_fruits_in_response', JsonInteger::make())
->property('number_of_fruits_in_response_divided_by_three', JsonNumber::make())
->property('is_number_of_fruits_in_response_even', JsonBoolean::make())
->property('fruit_most_occurring_color', JsonEnum::make()->enums(['red', 'green', 'blue']))
->property(
'random_integer_or_string_max_one_character',
JsonAnyOf::make()
->schema(JsonInteger::make())
->schema(JsonString::make())
),
),
)->json();
```

With raw JSON schema:

```php
$response = OpenAI::chat()->create(
model: 'gpt-4o-mini',
messages: Messages::make()->system('You are a helpful assistant.')->user('Suggest me tasty fruits.'),
responseFormat: ResponseFormat::make()->jsonSchema([
'name' => 'response',
'strict' => true,
'schema' => [
'type' => 'object',
'properties' => [
'fruits' => [
'type' => 'array',
'items' => [
'type' => 'string',
],
],
],
'additionalProperties' => false,
'required' => ['fruits'],
],
]),
)->json();
```

#### Streaming
Expand All @@ -116,7 +165,7 @@ $response = OpenAI::chat()->stream(function (string $newChunk, string $message)
echo $newChunk;
})->create(
model: 'gpt-3.5-turbo',
messages: (new Messages)->system('You are a helpful assistant.')->user('Hello!'),
messages: Messages::make()->system('You are a helpful assistant.')->user('Hello!'),
);
```

Expand Down Expand Up @@ -164,6 +213,23 @@ Retrieving a file content.
$fileContent = OpenAI::files()->retrieveContent($file->id);
```

### Vector Stores

```php
$filePath = __DIR__ . '/../test.txt';
$file = OpenAI::files()->upload(
file_get_contents($filePath),
basename($filePath),
'assistants',
);

$vectorStore = OpenAI::vectorStores()->create([$file->id]);
$vectorStores = OpenAI::vectorStores()->list();
$vectorStoreRetrieved = OpenAI::vectorStores()->retrieve($vectorStore->id);
$vectorStoreModified = OpenAI::vectorStores()->modify($vectorStore->id, 'Modified vector store');
$vectorStoreDeleted = OpenAI::vectorStores()->delete($vectorStore->id);
```

### Threads

```php
Expand All @@ -174,11 +240,10 @@ $assistant = OpenAI::assistants()->create(
'You are a kindergarten teacher. When asked a questions, anwser shortly and as a young child could understand.'
);
$thread = OpenAI::threads()
->create((new ThreadMessages)->user('What is your purpose in one short sentence?'));
->create(Messages::make()->user('What is your purpose in one short sentence?'));
$message = OpenAI::threads()->messages()
->create($thread->id, ThreadMessage::user('How does AI work? Explain it in simple terms in one sentence.'));
$run = OpenAI::threads()->run()->execute($thread->id, $assistant->id)->wait();
$messages = OpenAI::threads()->messages()->list($thread->id);
$response = OpenAI::threads()->run()->execute($thread->id, $assistant->id)->wait()->json();

// cleanup
$deletedThread = OpenAI::threads()->delete($thread->id);
Expand Down
4 changes: 4 additions & 0 deletions src/Capabilities/Threads/Parameters/Messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace Outl1ne\NovaOpenAI\Capabilities\Threads\Parameters;

use Outl1ne\NovaOpenAI\Traits\StaticMake;

class Messages
{
use StaticMake;

public array $messages = [];

public function user(string $content, ?array $attachments = null, ?array $metadata = null): self
Expand Down
16 changes: 16 additions & 0 deletions src/Capabilities/Threads/Responses/MessagesResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@ public function __construct(...$arguments)
$this->appendMeta('has_more', $this->data['has_more']);
$this->messages = $this->data['data'];
}

public function json(): ?object
{
return json_decode($this->messages[0]['content'][0]['text']['value']);
}

public function thread(): ?array
{
return collect($this->messages)->map(function ($message) {
$messageValue = $message['content'][0]['text']['value'];
return [
'role' => $message['role'],
'message' => json_validate($messageValue) ? json_decode($messageValue) : $messageValue,
];
})->toArray();
}
}
5 changes: 3 additions & 2 deletions src/Capabilities/Threads/Responses/RunResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function __construct(...$arguments)
$this->appendMeta('required_action', $this->data['required_action']);
}

public function wait(?callable $errorCallback = null): RunResponse
public function wait(?callable $errorCallback = null): MessagesResponse
{
$status = null;
while ($status !== 'completed') {
Expand All @@ -63,6 +63,7 @@ public function wait(?callable $errorCallback = null): RunResponse
// sleep for 100ms
usleep(100_000);
}
return $runStatus;

return OpenAI::threads()->messages()->list($this->data['thread_id']);
}
}
27 changes: 27 additions & 0 deletions tests/Unit/AssistantTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

use Outl1ne\NovaOpenAI\Facades\OpenAI;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Outl1ne\NovaOpenAI\Capabilities\Threads\Parameters\Messages;
use Outl1ne\NovaOpenAI\Capabilities\Files\Responses\FileResponse;
use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\ResponseFormat;
use Outl1ne\NovaOpenAI\Capabilities\Files\Responses\FileDeleteResponse;
use Outl1ne\NovaOpenAI\Capabilities\Assistants\Responses\DeleteResponse;
use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonObject;
use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonString;
use Outl1ne\NovaOpenAI\Capabilities\Assistants\Responses\AssistantResponse;
use Outl1ne\NovaOpenAI\Capabilities\Assistants\Responses\AssistantFileResponse;
use Outl1ne\NovaOpenAI\Capabilities\Assistants\Responses\AssistantListResponse;
Expand Down Expand Up @@ -81,4 +85,27 @@ public function test_assistant_files(): void
$deletedFile = OpenAI::files()->delete($file->id);
$this->assertTrue($deletedFile instanceof FileDeleteResponse);
}

public function test_assistant_response_format_json_schema(): void
{
$assistant = OpenAI::assistants()->create(
'gpt-4o-mini',
'Allan\'s assistant',
'For testing purposes of nova-openai package.',
'You are a kindergarten teacher. When asked a questions, anwser shortly and as a young child could understand.',
responseFormat: ResponseFormat::make()->jsonSchema(
JsonObject::make()
->property('answer', JsonString::make())
),
);

$thread = OpenAI::threads()
->create(Messages::make()->user('What is your purpose in one short sentence?'));
$response = OpenAI::threads()->run()->execute($thread->id, $assistant->id)->wait();

$this->assertIsString($response->json()?->answer);

OpenAI::threads()->delete($thread->id);
OpenAI::assistants()->delete($assistant->id);
}
}
10 changes: 5 additions & 5 deletions tests/Unit/ChatTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function test_chat(): void
{
$response = OpenAI::chat()->create(
model: 'gpt-4o-mini',
messages: (new Messages)->system('You are a helpful assistant.')->user('Hello!'),
messages: Messages::make()->system('You are a helpful assistant.')->user('Hello!'),
);
$this->assertTrue($response instanceof ChatResponse);
$this->assertIsArray($response->choices);
Expand All @@ -42,8 +42,8 @@ public function test_chat_json_response(): void
{
$response = OpenAI::chat()->create(
model: 'gpt-4o-mini',
messages: (new Messages)->system('You are a helpful assistant.')->user('Suggest me tasty fruits as JSON array of fruits.'),
responseFormat: (new ResponseFormat)->json(),
messages: Messages::make()->system('You are a helpful assistant.')->user('Suggest me tasty fruits as JSON array of fruits.'),
responseFormat: ResponseFormat::make()->json(),
);
$this->assertTrue($response instanceof ChatResponse);
$this->assertIsArray($response->choices);
Expand Down Expand Up @@ -106,7 +106,7 @@ public function test_chat_stream(): void
{
$response = OpenAI::chat()->stream(function (string $newChunk, string $message) {})->create(
model: 'gpt-4o-mini',
messages: (new Messages)->system('You are a helpful assistant.')->user('Hello!'),
messages: Messages::make()->system('You are a helpful assistant.')->user('Hello!'),
);
$this->assertTrue($response instanceof StreamedChatResponse);
$this->assertIsArray($response->choices);
Expand All @@ -116,7 +116,7 @@ public function test_chat_image_url(): void
{
$response = OpenAI::chat()->create(
model: 'gpt-4o-mini',
messages: (new Messages)->system('You are a helpful assistant.')->user([
messages: Messages::make()->system('You are a helpful assistant.')->user([
[
'type' => 'text',
'text' => 'Describe what\'s on the attached photo',
Expand Down
8 changes: 2 additions & 6 deletions tests/Unit/ThreadsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function test_threads(): void
$this->assertTrue($assistant instanceof AssistantResponse);

$thread = OpenAI::threads()
->create((new Messages)->user('What is your purpose in one short sentence?'));
->create(Messages::make()->user('What is your purpose in one short sentence?'));
$this->assertTrue($thread instanceof ThreadResponse);

$message = OpenAI::threads()->messages()
Expand All @@ -59,11 +59,7 @@ public function test_threads(): void
]));
$this->assertTrue($message2 instanceof MessageResponse);

$run = OpenAI::threads()->run()->execute($thread->id, $assistant->id)->wait();
$this->assertTrue($run instanceof RunResponse);
$this->assertEquals($run->meta['status'], 'completed');

$messages = OpenAI::threads()->messages()->list($thread->id);
$messages = OpenAI::threads()->run()->execute($thread->id, $assistant->id)->wait();
$this->assertTrue($messages instanceof MessagesResponse);

// cleanup
Expand Down

0 comments on commit 2b7f7b3

Please sign in to comment.