Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
8 changes: 7 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "openai-php/client",
"name": "lhty03/openai-php",
"description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API",
"keywords": ["php", "openai", "sdk", "codex", "GPT-3", "DALL-E", "api", "client", "natural", "language", "processing"],
"license": "MIT",
Expand All @@ -21,6 +21,12 @@
"psr/http-factory-implementation": "*",
"psr/http-message": "^1.1.0|^2.0.0"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/LHTY03/openai-php"
}
],
"require-dev": {
"guzzlehttp/guzzle": "^7.9.2",
"guzzlehttp/psr7": "^2.7.0",
Expand Down
File renamed without changes.
41 changes: 40 additions & 1 deletion documentations/otherAPI.md → documentations/New_LLMs.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ $client = OpenAI::factory()
->withProvider('grok') // Could be Grok, grok, GROK, but anything else would be set to openai
->make();
```
Now this sets the default baseURL to Grok's baseURL, and instead of taking away the function that allows
Now this sets the default baseURL to Groks baseURL, and instead of taking away the function that allows
users to set upon their own url, this is just a quick fix that allows people to simply click in and change
the provider easily.

Expand Down Expand Up @@ -109,9 +109,48 @@ integrating Gemini and it would still require some work of progress.

## Perplexity:

### General View over the API
The Official documentation of Perplexity is the following:
https://docs.perplexity.ai/home

Perplexity is completely compatible when we are call perplexity through the openai library.
we would not have to do much to make perplexity compatible to openai-php library.
The official documentation states that perplexity is compatible, so all we have to change is simple,
which is making the call of the api easier. The below are the code that we tests which works
for calling the perplexity api:

```php
$client = OpenAI::factory()
->withApiKey($_ENV["PERPLEXITY_API_KEY"])
->withOrganization('Brainiest')
->withProject('RCOS_Brainiest')
->withProvider('perplexity')
->make();

$response = $client->chat()->create([
'model' => 'sonar-pro',
'messages' => [
['role' => 'user', 'content' => 'What is RCOS'],
],
]);

echo $response->choices[0]->message->content;
```
Since Perplexity official documents only display chat completion function on the api document, we could not
add in other functions such as image generation and function calling. In the future when we work on this project,
we would love to add in more function such as image generation.


## New Features

Additional Functions that we added except for other LLMs supports mostly lay in two fields, additional provider
field when creating the client and usage tracking.

### Provider Field
We initialized a field when creating the client for the LLM models, this withProvider function would allow the users
that does now know the exact LLM api address to call the different LLM models that we provide support easier. With
a simple call of the withProvider function, the user can call LLMs that we provide initial support to (Grok, Gemini
, Perplexity) easily without making more research on each LLMs documentation.



Expand Down
27 changes: 27 additions & 0 deletions documentations/Start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# How to use the new Functions

## With Provider

With provider is a new service that we added in for users to play with. When we are creating a client, instead of
naming the baseURL, we could name it through the LLM that we are selecting. so with the new withProvider service,
users could select different LLM's base urls through simply typing in the names of these LLM providers.

For the example below, instead of providing the baseURL for each LLM, we could simply add in the withProvider tag
to specify the baseURL for a specific LLM provider.
```php
$client = OpenAI::factory()
->withApiKey($_ENV["GROK_API_KEY"])
->withOrganization('your-organization') // default: null
->withProject('Your Project') // default: null
->withProvider('Grok') //->withBaseUri('https://api.x.ai/v1')
->make();
```

This function currently supports the following providers:
- Grok
- Gemini
- Perplexity
- OpenAI

We would work on expanding this functionality to more providers as we incorporate the different providers into the
availability of the library.
File renamed without changes.
36 changes: 27 additions & 9 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,19 @@
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpClient\Psr18Client;


final class Factory
{

private ?array $uri_map = [
'default' => 'api.openai.com/v1',
'x.ai' => 'https://api.x.ai/v1',
'grok' => 'https://api.x.ai/v1',
'gemini' => 'https://generativelanguage.googleapis.com/v1beta/openai',
'perplexity' => 'https://api.perplexity.ai',
'sonar' => 'https://api.perplexity.ai'
];

/**
* The API key for the requests.
*/
Expand Down Expand Up @@ -91,6 +102,12 @@ public function withOrganization(?string $organization): self
*/
public function withProject(?string $project): self
{
if ($project === null) {
$this->project = null;
return $this;
}

$project = strtolower(preg_replace('/[^a-zA-Z]/', '', $project));
$this->project = $project;

return $this;
Expand All @@ -101,6 +118,12 @@ public function withProject(?string $project): self
*/
public function withProvider(?string $project): self
{
if ($project === null) {
$this->provider = null;
return $this;
}

$project = strtolower(preg_replace('/[^a-zA-Z]/', '', $project));
$this->provider = $project;

return $this;
Expand Down Expand Up @@ -184,17 +207,12 @@ public function make(): Client
$headers = $headers->withCustomHeader($name, $value);
}

if ($this->provider !== null) {
if($this->provider == 'Grok' || $this->provider == 'grok'|| $this->provider == 'GROK'){
$baseUri = BaseUri::from($this->baseUri ?: 'api.openai.com/v1');
}

if (is_array($this->uri_map) && array_key_exists($this->provider, $this->uri_map)) {
$baseUri = BaseUri::from($this->uri_map[$this->provider]);
} else {
$baseUri = BaseUri::from($this->baseUri ?: 'https://api.x.ai/v1');
$baseUri = BaseUri::from($this->baseUri ?: 'api.openai.com/v1');
}




$queryParams = QueryParams::create();
foreach ($this->queryParams as $name => $value) {
$queryParams = $queryParams->withParam($name, $value);
Expand Down
2 changes: 2 additions & 0 deletions src/Responses/Chat/CreateResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static function from(array $attributes, MetaInformation $meta): self
$choices = array_map(fn (array $result): CreateResponseChoice => CreateResponseChoice::from(
$result
), $attributes['choices']);

$attributes['id'] = $attributes['id'] ?? "NA";

return new self(
$attributes['id'],
Expand Down
2 changes: 2 additions & 0 deletions src/Responses/Images/CreateResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public static function from(array $attributes, MetaInformation $meta): self
$results = array_map(fn (array $result): CreateResponseData => CreateResponseData::from(
$result
), $attributes['data']);

$attributes['created'] = $attributes['created'] ?? 0;

return new self(
$attributes['created'],
Expand Down
50 changes: 50 additions & 0 deletions tests/Testing/GrokTests/ChatTestResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

use OpenAI\Resources\Chat\CreateResponse;
use OpenAI\Factory;
use OpenAI\Client;

it('returns a response', function () {
$client = OpenAI::factory()
->withApiKey('')
->withOrganization('brainiest-testing')
->withProvider('grok')
->withProject('brainiest-testing')
->make();

$result = $client->chat()->create([
'model' => 'grok-2-latest',
'messages' => [
['role' => 'user', 'content' => 'Hello! How are you?'],
],
]);

# expect that the result is an instance of CreateResponse
#expect($result)->toBeInstanceOf(CreateResponse::class);

#expect the response to be not empty
expect($result['choices'][0]['message']['content'])->not->toBeEmpty();
});

it('accepts a system role message and returns a response', function () {
$client = OpenAI::factory()
->withApiKey('')
->withOrganization('brainiest-testing')
->withProvider('grok')
->withProject('brainiest-testing')
->make();

$result = $client->chat()->create([
'model' => 'grok-2-latest',
'messages' => [
['role' => 'system', 'content' => 'You are a helpful assistant.'],
['role' => 'user', 'content' => 'Hello! How are you?']
],
]);

# expect that the result is an instance of CreateResponse
#expect($result)->toBeInstanceOf(CreateResponse::class);

#expect the response to be not empty
expect($result['choices'][0]['message']['content'])->not->toBeEmpty();
});
41 changes: 41 additions & 0 deletions tests/Testing/GrokTests/CompletionsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

use OpenAI\Responses\Completions;
use OpenAI\Factory;
use OpenAI\Client;

it('returns a completions response with correct parameters', function () {
$client = OpenAI::factory()
->withApiKey('')
->withOrganization('brainiest-testing')
->withProvider('grok')
->withProject('brainiest-testing')
->make();

$response = $client->completions()->create([
'model' => 'grok-2-1212',
'prompt' => 'Write 3 sentences about lions',
'max_tokens' => 5,
'temperature' => 0
]);

# check that response has the correct class
expect($response)->toBeInstanceOf(\OpenAI\Responses\Completions\CreateResponse::class);
expect($response->id)->not->toBeEmpty();
expect($response->object)->toBe('text_completion');
expect($response->created)->not->toBeEmpty();
expect($response->model)->toBe('grok-2-1212');
expect($response->usage->promptTokens)->not->toBeEmpty();
expect($response->usage->completionTokens)->not->toBeEmpty();
expect($response->usage->totalTokens)->toBe($response->usage->promptTokens + $response->usage->completionTokens);

foreach ($response->choices as $choice) {
expect($choice->text)->not->toBeEmpty ; // '\n\nThis is a test'
#expect choice index to be an integer
expect($choice->index)->toBeInteger();
#TODO: TEST FINISH REASON AND LOG PROBS
}



});
49 changes: 49 additions & 0 deletions tests/Testing/GrokTests/ModelsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

use OpenAI\Resources\Models;
use OpenAI\Factory;
use OpenAI\Client;

it('returns a list of models', function () {
$client = OpenAI::factory()
->withApiKey('')
->withOrganization('brainiest-testing')
->withProvider('grok')
->withProject('brainiest-testing')
->make();

$response = $client->models()->list();

# expect response object to be a list
expect($response)->toBeInstanceOf(\OpenAI\Responses\Models\ListResponse::class);

#expect the response to be not empty
expect($response->object)->toBe('list');
expect($response->data)->not->toBeEmpty();
expect($response->data[0]->id)->not->toBeEmpty();
expect($response->data[0]->object)->toBe('model');
});

it('retreives a models attributes', function () {
$client = OpenAI::factory()
->withApiKey('')
->withOrganization('brainiest-testing')
->withProvider('grok')
->withProject('brainiest-testing')
->make();

$response = $client->models()->list();

# expect response object to be a list
expect($response)->toBeInstanceOf(\OpenAI\Responses\Models\ListResponse::class);

$model_name = $response->data[0]->id;
$response = $client->models()->retrieve($model_name);

#expect the response to be not empty
expect($response->id)->toBe($model_name);
expect($response->object)->toBe('model');
expect($response->created)->not->toBeEmpty();
expect($response->ownedBy)->toBe('xai');
});