This repository has been archived by the owner on Nov 28, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
153 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
<?php | ||
|
||
namespace HaoziTeam\ChatGPT; | ||
|
||
use Exception; | ||
use GuzzleHttp\Exception\GuzzleException; | ||
use Psr\Http\Message\StreamInterface; | ||
use GuzzleHttp\Client; | ||
|
||
class V2 | ||
{ | ||
private string $baseUrl = 'https://api.openai.com/'; | ||
private string $model = 'gpt-3.5-turbo'; | ||
private string $key; | ||
private int $temperature = 1; | ||
private int $topP = 1; | ||
private array $messages = []; | ||
private mixed $http; | ||
|
||
public function __construct(string $key, string $model = null, int $temperature = null, int $topP = null, int $timeout = 360) | ||
{ | ||
$this->key = $key; | ||
if ($model) { | ||
$this->model = $model; | ||
} | ||
if ($temperature) { | ||
$this->temperature = $temperature; | ||
} | ||
if ($topP) { | ||
$this->topP = $topP; | ||
} | ||
|
||
$this->http = new Client([ | ||
'base_uri' => $this->baseUrl, | ||
'timeout' => $timeout, | ||
'stream' => true, | ||
]); | ||
} | ||
|
||
/** | ||
* 添加消息 | ||
* @param string $message | ||
* @param string $role | ||
* @return void | ||
*/ | ||
public function addMessage(string $message, string $role = 'user'): void | ||
{ | ||
$this->messages[] = [ | ||
'role' => $role, | ||
'content' => $message, | ||
]; | ||
} | ||
|
||
/** | ||
* 发送消息 | ||
* @param string $prompt | ||
* @param string|null $user | ||
* @param bool $stream | ||
* @return array|StreamInterface | ||
* @throws Exception | ||
*/ | ||
public function ask(string $prompt, string $user = null, bool $stream = false): StreamInterface|array | ||
{ | ||
|
||
// 将消息添加到消息列表中 | ||
$this->addMessage($prompt); | ||
|
||
$data = [ | ||
'model' => $this->model, | ||
'messages' => $this->messages, | ||
'stream' => $stream, | ||
'temperature' => $this->temperature, | ||
'top_p' => $this->topP, | ||
'n' => 1, | ||
'user' => $user, | ||
]; | ||
|
||
try { | ||
$response = $this->http->post( | ||
'v1/chat/completions', | ||
[ | ||
'json' => $data, | ||
'headers' => [ | ||
'Authorization' => $this->key, | ||
], | ||
'stream' => $stream, | ||
] | ||
); | ||
} catch (GuzzleException $e) { | ||
throw new Exception($e->getMessage()); | ||
} | ||
|
||
// 如果是数据流模式,则直接返回数据流 | ||
if ($stream) { | ||
return $response->getBody(); | ||
} | ||
|
||
$data = json_decode($response->getBody()->getContents(), true); | ||
if (json_last_error() !== JSON_ERROR_NONE) { | ||
throw new Exception('Response is not json'); | ||
} | ||
|
||
if (!$this->checkFields($data)) { | ||
throw new Exception('Field missing'); | ||
} | ||
|
||
$answer = $data['choices'][0]['message']['content']; | ||
$this->addMessage($answer, 'assistant'); | ||
|
||
return [ | ||
'answer' => $answer, | ||
'id' => $data['id'], | ||
'model' => $this->model, | ||
'usage' => $data['usage'], | ||
]; | ||
} | ||
|
||
/** | ||
* 检查响应行是否包含必要的字段 | ||
* @param mixed $line | ||
* @return bool | ||
*/ | ||
public function checkFields(mixed $line): bool | ||
{ | ||
return isset($line['choices'][0]['message']['content']) && isset($line['id']) && isset($line['usage']); | ||
} | ||
|
||
/** | ||
* 格式化流消息为数组 | ||
* @param string $line | ||
* @return array|false | ||
*/ | ||
public function formatStreamMessage(string $line): array|false | ||
{ | ||
preg_match('/data: (.*)/', $line, $matches); | ||
if (empty($matches[1])) { | ||
return false; | ||
} | ||
$line = $matches[1]; | ||
$line = str_replace('\\"', '"', $line); | ||
$line = str_replace("\\'", "'", $line); | ||
$line = str_replace("\\\\", "\\", $line); | ||
|
||
$data = json_decode($line, true); | ||
|
||
if (json_last_error() !== JSON_ERROR_NONE) { | ||
return false; | ||
} | ||
|
||
return $data; | ||
} | ||
|
||
} |