Skip to content

Commit

Permalink
Add support for tool_calls on the chat completion endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
gehrisandro committed Nov 7, 2023
1 parent 03a24a6 commit cf6c443
Show file tree
Hide file tree
Showing 19 changed files with 428 additions and 11 deletions.
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,60 @@ $response->usage->totalTokens; // 21
$response->toArray(); // ['id' => 'chatcmpl-6pMyfj1HF4QXnfvjtfzvufZSQq6Eq', ...]
```

Creates a completion for the chat message with a tool call.

```php
$response = $client->chat()->create([
'model' => 'gpt-3.5-turbo-0613',
'messages' => [
['role' => 'user', 'content' => 'What\'s the weather like in Boston?'],
],
'tools' => [
[
'type' => 'function',
'function' => [
'name' => 'get_current_weather',
'description' => 'Get the current weather in a given location',
'parameters' => [
'type' => 'object',
'properties' => [
'location' => [
'type' => 'string',
'description' => 'The city and state, e.g. San Francisco, CA',
],
'unit' => [
'type' => 'string',
'enum' => ['celsius', 'fahrenheit']
],
],
'required' => ['location'],
],
],
]
]
]);

$response->id; // 'chatcmpl-6pMyfj1HF4QXnfvjtfzvufZSQq6Eq'
$response->object; // 'chat.completion'
$response->created; // 1677701073
$response->model; // 'gpt-3.5-turbo-0613'

foreach ($response->choices as $result) {
$result->index; // 0
$result->message->role; // 'assistant'
$result->message->content; // null
$result->message->toolCalls[0]->id; // 'call_123'
$result->message->toolCalls[0]->type; // 'function'
$result->message->toolCalls[0]->function->name; // 'get_current_weather'
$result->message->toolCalls[0]->function->arguments; // "{\n \"location\": \"Boston, MA\"\n}"
$result->finishReason; // 'tool_calls'
}

$response->usage->promptTokens; // 82,
$response->usage->completionTokens; // 18,
$response->usage->totalTokens; // 100
```

Creates a completion for the chat message with a function call.

```php
Expand Down
2 changes: 1 addition & 1 deletion src/Resources/Chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function create(array $parameters): CreateResponse

$payload = Payload::create('chat/completions', $parameters);

/** @var Response<array{id: string, object: string, created: int, model: string, choices: array<int, array{index: int, message: array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}}> $response */
/** @var Response<array{id: string, object: string, created: int, model: string, system_fingerprint?: string, choices: array<int, array{index: int, message: array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}, tool_calls: ?array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}}> $response */
$response = $this->transporter->requestObject($payload);

return CreateResponse::from($response->data(), $response->meta());
Expand Down
13 changes: 8 additions & 5 deletions src/Responses/Chat/CreateResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
use OpenAI\Testing\Responses\Concerns\Fakeable;

/**
* @implements ResponseContract<array{id: string, object: string, created: int, model: string, choices: array<int, array{index: int, message: array{role: string, content: string|null, function_call?: array{name: string, arguments: string}}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}}>
* @implements ResponseContract<array{id: string, object: string, created: int, model: string, system_fingerprint?: string, choices: array<int, array{index: int, message: array{role: string, content: string|null, function_call?: array{name: string, arguments: string}, tool_calls?: array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}}>
*/
final class CreateResponse implements ResponseContract, ResponseHasMetaInformationContract
{
/**
* @use ArrayAccessible<array{id: string, object: string, created: int, model: string, choices: array<int, array{index: int, message: array{role: string, content: string|null, function_call?: array{name: string, arguments: string}}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}}>
* @use ArrayAccessible<array{id: string, object: string, created: int, model: string, system_fingerprint?: string, choices: array<int, array{index: int, message: array{role: string, content: string|null, function_call?: array{name: string, arguments: string}, tool_calls?: array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}}>
*/
use ArrayAccessible;

Expand All @@ -32,6 +32,7 @@ private function __construct(
public readonly string $object,
public readonly int $created,
public readonly string $model,
public readonly ?string $systemFingerprint,
public readonly array $choices,
public readonly CreateResponseUsage $usage,
private readonly MetaInformation $meta,
Expand All @@ -41,7 +42,7 @@ private function __construct(
/**
* Acts as static factory, and returns a new Response instance.
*
* @param array{id: string, object: string, created: int, model: string, choices: array<int, array{index: int, message: array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}} $attributes
* @param array{id: string, object: string, created: int, model: string, system_fingerprint?: string, choices: array<int, array{index: int, message: array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}, tool_calls: ?array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>}, finish_reason: string|null}>, usage: array{prompt_tokens: int, completion_tokens: int|null, total_tokens: int}} $attributes
*/
public static function from(array $attributes, MetaInformation $meta): self
{
Expand All @@ -54,6 +55,7 @@ public static function from(array $attributes, MetaInformation $meta): self
$attributes['object'],
$attributes['created'],
$attributes['model'],
$attributes['system_fingerprint'] ?? null,
$choices,
CreateResponseUsage::from($attributes['usage']),
$meta,
Expand All @@ -65,16 +67,17 @@ public static function from(array $attributes, MetaInformation $meta): self
*/
public function toArray(): array
{
return [
return array_filter([
'id' => $this->id,
'object' => $this->object,
'created' => $this->created,
'model' => $this->model,
'system_fingerprint' => $this->systemFingerprint,
'choices' => array_map(
static fn (CreateResponseChoice $result): array => $result->toArray(),
$this->choices,
),
'usage' => $this->usage->toArray(),
];
], fn (mixed $value): bool => ! is_null($value));
}
}
4 changes: 2 additions & 2 deletions src/Responses/Chat/CreateResponseChoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private function __construct(
}

/**
* @param array{index: int, message: array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}}, finish_reason: string|null} $attributes
* @param array{index: int, message: array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}, tool_calls: ?array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>}, finish_reason: string|null} $attributes
*/
public static function from(array $attributes): self
{
Expand All @@ -26,7 +26,7 @@ public static function from(array $attributes): self
}

/**
* @return array{index: int, message: array{role: string, content: string|null, function_call?: array{name: string, arguments: string}}, finish_reason: string|null}
* @return array{index: int, message: array{role: string, content: string|null, function_call?: array{name: string, arguments: string}, tool_calls?: array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>}, finish_reason: string|null}
*/
public function toArray(): array
{
Expand Down
3 changes: 3 additions & 0 deletions src/Responses/Chat/CreateResponseFunctionCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace OpenAI\Responses\Chat;

/**
* @deprecated
*/
final class CreateResponseFunctionCall
{
private function __construct(
Expand Down
17 changes: 15 additions & 2 deletions src/Responses/Chat/CreateResponseMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,36 @@

final class CreateResponseMessage
{
/**
* @param array<int, CreateResponseToolCall> $toolCalls
*/
private function __construct(
public readonly string $role,
public readonly ?string $content,
public readonly array $toolCalls,
public readonly ?CreateResponseFunctionCall $functionCall,
) {
}

/**
* @param array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}} $attributes
* @param array{role: string, content: ?string, function_call: ?array{name: string, arguments: string}, tool_calls: ?array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>} $attributes
*/
public static function from(array $attributes): self
{
$toolCalls = array_map(fn (array $result): CreateResponseToolCall => CreateResponseToolCall::from(
$result
), $attributes['tool_calls'] ?? []);

return new self(
$attributes['role'],
$attributes['content'] ?? null,
$toolCalls,
isset($attributes['function_call']) ? CreateResponseFunctionCall::from($attributes['function_call']) : null,
);
}

/**
* @return array{role: string, content: string|null, function_call?: array{name: string, arguments: string}}
* @return array{role: string, content: string|null, function_call?: array{name: string, arguments: string}, tool_calls?: array<int, array{id: string, type: string, function: array{name: string, arguments: string}}>}
*/
public function toArray(): array
{
Expand All @@ -39,6 +48,10 @@ public function toArray(): array
$data['function_call'] = $this->functionCall->toArray();
}

if ($this->toolCalls !== []) {
$data['tool_calls'] = array_map(fn (CreateResponseToolCall $toolCall): array => $toolCall->toArray(), $this->toolCalls);
}

return $data;
}
}
39 changes: 39 additions & 0 deletions src/Responses/Chat/CreateResponseToolCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace OpenAI\Responses\Chat;

final class CreateResponseToolCall
{
private function __construct(
public readonly string $id,
public readonly string $type,
public readonly CreateResponseToolCallFunction $function,
) {
}

/**
* @param array{id: string, type: string, function: array{name: string, arguments: string}} $attributes
*/
public static function from(array $attributes): self
{
return new self(
$attributes['id'],
$attributes['type'],
CreateResponseToolCallFunction::from($attributes['function']),
);
}

/**
* @return array{id: string, type: string, function: array{name: string, arguments: string}}
*/
public function toArray(): array
{
return [
'id' => $this->id,
'type' => $this->type,
'function' => $this->function->toArray(),
];
}
}
36 changes: 36 additions & 0 deletions src/Responses/Chat/CreateResponseToolCallFunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace OpenAI\Responses\Chat;

final class CreateResponseToolCallFunction
{
private function __construct(
public readonly string $name,
public readonly string $arguments,
) {
}

/**
* @param array{name: string, arguments: string} $attributes
*/
public static function from(array $attributes): self
{
return new self(
$attributes['name'],
$attributes['arguments'],
);
}

/**
* @return array{name: string, arguments: string}
*/
public function toArray(): array
{
return [
'name' => $this->name,
'arguments' => $this->arguments,
];
}
}
15 changes: 14 additions & 1 deletion src/Responses/Chat/CreateStreamedResponseDelta.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,30 @@

final class CreateStreamedResponseDelta
{
/**
* @param array<int, CreateStreamedResponseToolCall> $toolCalls
*/
private function __construct(
public readonly ?string $role,
public readonly ?string $content,
public readonly array $toolCalls,
public readonly ?CreateStreamedResponseFunctionCall $functionCall,
) {
}

/**
* @param array{role?: string, content?: string, function_call?: array{name?: ?string, arguments?: ?string}} $attributes
* @param array{role?: string, content?: string, function_call?: array{name?: ?string, arguments?: ?string}, tool_calls?: array<int, array{id?: string, type?: string, function: array{name?: string, arguments: string}}>} $attributes
*/
public static function from(array $attributes): self
{
$toolCalls = array_map(fn (array $result): CreateStreamedResponseToolCall => CreateStreamedResponseToolCall::from(
$result
), $attributes['tool_calls'] ?? []);

return new self(
$attributes['role'] ?? null,
$attributes['content'] ?? null,
$toolCalls,
isset($attributes['function_call']) ? CreateStreamedResponseFunctionCall::from($attributes['function_call']) : null,
);
}
Expand All @@ -40,6 +49,10 @@ public function toArray(): array
$data['function_call'] = $this->functionCall->toArray();
}

if ($this->toolCalls !== []) {
$data['tool_calls'] = array_map(fn (CreateStreamedResponseToolCall $toolCall): array => $toolCall->toArray(), $this->toolCalls);
}

return $data;
}
}
3 changes: 3 additions & 0 deletions src/Responses/Chat/CreateStreamedResponseFunctionCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace OpenAI\Responses\Chat;

/**
* @deprecated
*/
final class CreateStreamedResponseFunctionCall
{
private function __construct(
Expand Down
39 changes: 39 additions & 0 deletions src/Responses/Chat/CreateStreamedResponseToolCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace OpenAI\Responses\Chat;

final class CreateStreamedResponseToolCall
{
private function __construct(
public readonly ?string $id,
public readonly ?string $type,
public readonly CreateStreamedResponseToolCallFunction $function,
) {
}

/**
* @param array{id?: string, type?: string, function: array{name?: string, arguments: string}} $attributes
*/
public static function from(array $attributes): self
{
return new self(
$attributes['id'] ?? null,
$attributes['type'] ?? null,
CreateStreamedResponseToolCallFunction::from($attributes['function']),
);
}

/**
* @return array{id?: string, type?: string, function?: array{name?: string, arguments: string}}
*/
public function toArray(): array
{
return array_filter([
'id' => $this->id,
'type' => $this->type,
'function' => $this->function->toArray(),
]);
}
}
Loading

0 comments on commit cf6c443

Please sign in to comment.