From 72a83ad2ae1ba4bc6348887f303da4089ba1a1ce Mon Sep 17 00:00:00 2001 From: EJLin Date: Thu, 21 Nov 2024 16:38:55 +0800 Subject: [PATCH] feat: add openai compatibility support for google gemini --- src/Responses/Chat/CreateResponse.php | 8 +- src/Responses/Chat/CreateStreamedResponse.php | 4 +- src/Responses/Embeddings/CreateResponse.php | 4 +- .../Embeddings/CreateResponseEmbedding.php | 4 +- tests/Fixtures/Chat.php | 76 +++++++++++++++++++ tests/Fixtures/Embedding.php | 29 +++++++ tests/Responses/Chat/CreateResponse.php | 32 ++++++++ .../Responses/Chat/CreateStreamedResponse.php | 13 ++++ tests/Responses/Embeddings/CreateResponse.php | 14 ++++ .../Embeddings/CreateResponseEmbedding.php | 13 ++++ 10 files changed, 187 insertions(+), 10 deletions(-) diff --git a/src/Responses/Chat/CreateResponse.php b/src/Responses/Chat/CreateResponse.php index 3d424e8b..6d9c99dd 100644 --- a/src/Responses/Chat/CreateResponse.php +++ b/src/Responses/Chat/CreateResponse.php @@ -28,13 +28,13 @@ final class CreateResponse implements ResponseContract, ResponseHasMetaInformati * @param array $choices */ private function __construct( - public readonly string $id, + public readonly ?string $id, public readonly string $object, public readonly int $created, public readonly string $model, public readonly ?string $systemFingerprint, public readonly array $choices, - public readonly CreateResponseUsage $usage, + public readonly ?CreateResponseUsage $usage, private readonly MetaInformation $meta, ) {} @@ -50,13 +50,13 @@ public static function from(array $attributes, MetaInformation $meta): self ), $attributes['choices']); return new self( - $attributes['id'], + $attributes['id'] ?? null, $attributes['object'], $attributes['created'], $attributes['model'], $attributes['system_fingerprint'] ?? null, $choices, - CreateResponseUsage::from($attributes['usage']), + isset($attributes['usage']) ? CreateResponseUsage::from($attributes['usage']) : null, $meta, ); } diff --git a/src/Responses/Chat/CreateStreamedResponse.php b/src/Responses/Chat/CreateStreamedResponse.php index adce31d7..afb5150a 100644 --- a/src/Responses/Chat/CreateStreamedResponse.php +++ b/src/Responses/Chat/CreateStreamedResponse.php @@ -24,7 +24,7 @@ final class CreateStreamedResponse implements ResponseContract * @param array $choices */ private function __construct( - public readonly string $id, + public readonly ?string $id, public readonly string $object, public readonly int $created, public readonly string $model, @@ -44,7 +44,7 @@ public static function from(array $attributes): self ), $attributes['choices']); return new self( - $attributes['id'], + $attributes['id'] ?? null, $attributes['object'], $attributes['created'], $attributes['model'], diff --git a/src/Responses/Embeddings/CreateResponse.php b/src/Responses/Embeddings/CreateResponse.php index 7c734f12..2762550c 100644 --- a/src/Responses/Embeddings/CreateResponse.php +++ b/src/Responses/Embeddings/CreateResponse.php @@ -30,7 +30,7 @@ final class CreateResponse implements ResponseContract, ResponseHasMetaInformati private function __construct( public readonly string $object, public readonly array $embeddings, - public readonly CreateResponseUsage $usage, + public readonly ?CreateResponseUsage $usage, private readonly MetaInformation $meta, ) {} @@ -48,7 +48,7 @@ public static function from(array $attributes, MetaInformation $meta): self return new self( $attributes['object'], $embeddings, - CreateResponseUsage::from($attributes['usage']), + isset($attributes['usage']) ? CreateResponseUsage::from($attributes['usage']) : null, $meta, ); } diff --git a/src/Responses/Embeddings/CreateResponseEmbedding.php b/src/Responses/Embeddings/CreateResponseEmbedding.php index 2c6c4635..e7816c88 100644 --- a/src/Responses/Embeddings/CreateResponseEmbedding.php +++ b/src/Responses/Embeddings/CreateResponseEmbedding.php @@ -11,7 +11,7 @@ final class CreateResponseEmbedding */ private function __construct( public readonly string $object, - public readonly int $index, + public readonly ?int $index, public readonly array $embedding, ) {} @@ -22,7 +22,7 @@ public static function from(array $attributes): self { return new self( $attributes['object'], - $attributes['index'], + $attributes['index'] ?? null, $attributes['embedding'], ); } diff --git a/tests/Fixtures/Chat.php b/tests/Fixtures/Chat.php index 9d9c5ed5..aa0138c1 100644 --- a/tests/Fixtures/Chat.php +++ b/tests/Fixtures/Chat.php @@ -36,6 +36,64 @@ function chatCompletion(): array ]; } +/** + * @return array + */ +function chatCompletionWithoutId(): array +{ + return [ + 'object' => 'chat.completion', + 'created' => 1677652288, + 'model' => 'gpt-3.5-turbo', + 'choices' => [ + [ + 'index' => 0, + 'message' => [ + 'role' => 'assistant', + 'content' => "\n\nHello there, how may I assist you today?", + ], + 'finish_reason' => 'stop', + ], + ], + 'usage' => [ + 'prompt_tokens' => 9, + 'completion_tokens' => 12, + 'total_tokens' => 21, + 'prompt_tokens_details' => [ + 'cached_tokens' => 5, + ], + 'completion_tokens_details' => [ + 'reasoning_tokens' => 0, + 'accepted_prediction_tokens' => 0, + 'rejected_prediction_tokens' => 0, + ], + ], + ]; +} + +/** + * @return array + */ +function chatCompletionWithoutUsage(): array +{ + return [ + 'id' => 'chatcmpl-123', + 'object' => 'chat.completion', + 'created' => 1677652288, + 'model' => 'gpt-3.5-turbo', + 'choices' => [ + [ + 'index' => 0, + 'message' => [ + 'role' => 'assistant', + 'content' => "\n\nHello there, how may I assist you today?", + ], + 'finish_reason' => 'stop', + ], + ], + ]; +} + /** * @return array */ @@ -195,6 +253,24 @@ function chatCompletionStreamFirstChunk(): array ]; } +function chatCompletionStreamFirstChunkWithoutId(): array +{ + return [ + 'object' => 'chat.completion.chunk', + 'created' => 1679432086, + 'model' => 'gpt-4-0314', + 'choices' => [ + [ + 'index' => 0, + 'delta' => [ + 'role' => 'assistant', + ], + 'finish_reason' => null, + ], + ], + ]; +} + function chatCompletionStreamContentChunk(): array { return [ diff --git a/tests/Fixtures/Embedding.php b/tests/Fixtures/Embedding.php index 659f6a69..dbddee7e 100644 --- a/tests/Fixtures/Embedding.php +++ b/tests/Fixtures/Embedding.php @@ -16,6 +16,21 @@ function embedding(): array ]; } +/** + * @return array + */ +function embeddingWithoutIndex(): array +{ + return [ + 'object' => 'embedding', + 'embedding' => [ + -0.008906792, + -0.013743395, + 0.009874112, + ], + ]; +} + /** * @return array */ @@ -33,3 +48,17 @@ function embeddingList(): array ], ]; } + +/** + * @return array + */ +function embeddingListWithoutUsage(): array +{ + return [ + 'object' => 'list', + 'data' => [ + embedding(), + embedding(), + ], + ]; +} diff --git a/tests/Responses/Chat/CreateResponse.php b/tests/Responses/Chat/CreateResponse.php index 565d72c0..caafedf5 100644 --- a/tests/Responses/Chat/CreateResponse.php +++ b/tests/Responses/Chat/CreateResponse.php @@ -21,6 +21,38 @@ ->meta()->toBeInstanceOf(MetaInformation::class); }); +test('from without id', function () { + $completion = CreateResponse::from(chatCompletionWithoutId(), meta()); + + expect($completion) + ->toBeInstanceOf(CreateResponse::class) + ->id->toBeNull() + ->object->toBe('chat.completion') + ->created->toBe(1677652288) + ->model->toBe('gpt-3.5-turbo') + ->systemFingerprint->toBeNull() + ->choices->toBeArray()->toHaveCount(1) + ->choices->each->toBeInstanceOf(CreateResponseChoice::class) + ->usage->toBeInstanceOf(CreateResponseUsage::class) + ->meta()->toBeInstanceOf(MetaInformation::class); +}); + +test('from without usage', function () { + $completion = CreateResponse::from(chatCompletionWithoutUsage(), meta()); + + expect($completion) + ->toBeInstanceOf(CreateResponse::class) + ->id->toBe('chatcmpl-123') + ->object->toBe('chat.completion') + ->created->toBe(1677652288) + ->model->toBe('gpt-3.5-turbo') + ->systemFingerprint->toBeNull() + ->choices->toBeArray()->toHaveCount(1) + ->choices->each->toBeInstanceOf(CreateResponseChoice::class) + ->usage->toBeNull() + ->meta()->toBeInstanceOf(MetaInformation::class); +}); + test('from with system fingerprint', function () { $completion = CreateResponse::from(chatCompletionWithSystemFingerprint(), meta()); diff --git a/tests/Responses/Chat/CreateStreamedResponse.php b/tests/Responses/Chat/CreateStreamedResponse.php index 4995400b..80a09e54 100644 --- a/tests/Responses/Chat/CreateStreamedResponse.php +++ b/tests/Responses/Chat/CreateStreamedResponse.php @@ -17,6 +17,19 @@ ->choices->each->toBeInstanceOf(CreateStreamedResponseChoice::class); }); +test('from without id', function () { + $completion = CreateStreamedResponse::from(chatCompletionStreamFirstChunkWithoutId()); + + expect($completion) + ->toBeInstanceOf(CreateStreamedResponse::class) + ->id->toBeNull() + ->object->toBe('chat.completion.chunk') + ->created->toBe(1679432086) + ->model->toBe('gpt-4-0314') + ->choices->toBeArray()->toHaveCount(1) + ->choices->each->toBeInstanceOf(CreateStreamedResponseChoice::class); +}); + test('from usage chunk', function () { $completion = CreateStreamedResponse::from(chatCompletionStreamUsageChunk()); diff --git a/tests/Responses/Embeddings/CreateResponse.php b/tests/Responses/Embeddings/CreateResponse.php index 8c6b120a..f495392e 100644 --- a/tests/Responses/Embeddings/CreateResponse.php +++ b/tests/Responses/Embeddings/CreateResponse.php @@ -1,6 +1,7 @@ object->toBe('list') ->embeddings->toBeArray()->toHaveCount(2) ->embeddings->each->toBeInstanceOf(CreateResponseEmbedding::class) + ->usage->toBeInstanceOf(CreateResponseUsage::class) + ->meta()->toBeInstanceOf(MetaInformation::class); +}); + +test('from without usage', function () { + $response = CreateResponse::from(embeddingListWithoutUsage(), meta()); + + expect($response) + ->toBeInstanceOf(CreateResponse::class) + ->object->toBe('list') + ->embeddings->toBeArray()->toHaveCount(2) + ->embeddings->each->toBeInstanceOf(CreateResponseEmbedding::class) + ->usage->toBeNull() ->meta()->toBeInstanceOf(MetaInformation::class); }); diff --git a/tests/Responses/Embeddings/CreateResponseEmbedding.php b/tests/Responses/Embeddings/CreateResponseEmbedding.php index 220658aa..a5fed133 100644 --- a/tests/Responses/Embeddings/CreateResponseEmbedding.php +++ b/tests/Responses/Embeddings/CreateResponseEmbedding.php @@ -15,6 +15,19 @@ ]); }); +test('from without index', function () { + $result = CreateResponseEmbedding::from(embeddingWithoutIndex()); + + expect($result) + ->object->toBe('embedding') + ->index->toBeNull() + ->embedding->toBeArray()->toBe([ + -0.008906792, + -0.013743395, + 0.009874112, + ]); +}); + test('to array', function () { $result = CreateResponseEmbedding::from(embedding());