Files
f7cloud_client/apps/gigachat_integration/lib/Service/GigaChatService.php
T
root 8b6a0139db f7cloud_client
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 22:59:26 +00:00

181 lines
6.2 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\GigaChat\Service;
use OCA\GigaChat\AppInfo\Application;
use OCP\Files\IRootFolder;
use OCP\Files\File;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
/**
* Класс для работы с GigaChat (Сбер)
* Документация: https://developers.sber.ru/docs/ru/gigachat/api/reference/rest/gigachat-api
*/
class GigaChatService {
private const
TOKEN_URL = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth',
METHOD_URL = 'https://gigachat.devices.sberbank.ru/api/v1/',
TOKEN_FILE = 'token.json',
CONFIG_ACCESS_SCOPE = 'gigachat_integration_scope',
CONFIG_ACCESS_KEY = 'gigachat_integration_key',
CONFIG_MODEL = 'gigachat_integration_model',
DEFAULT_MODEL = 'GigaChat',
MAX_TOKENS = 100;
public const
ROLES = [
'user',
'assistant'
];
public function __construct(
private IConfig $config,
private IClientService $clientService,
private IRootFolder $rootFolder,
) {
}
public function get_config_key(string $key, string $default = ''): string {
$result = $this->config->getSystemValue($key);
return empty($result) ? $default : $result;
}
public function get_response(string $message, array $history = []): string {
$result = '';
if (empty($message)) {
throw new \Exception('Empty message');
}
$data = [
// 'max_tokens' => self::MAX_TOKENS,
'model' => $this->get_config_key(self::CONFIG_MODEL, self::DEFAULT_MODEL),
'function_call' => 'none',
'messages' => [
[
'role' => 'system',
'content' => 'Ты ассистент с искусственным интелектом в системе F7cloud',
],
],
];
foreach ($history as $item) {
if (
!empty($item['content'])
&& !empty($item['role'])
&& in_array($item['role'], self::ROLES)
) {
$data['messages'][] = $item;
}
}
$data['messages'][] = [
'role' => 'user',
'content' => $message,
];
try {
$response_data = $this->send_request('chat/completions', $data);
foreach ($response_data['choices'] as $choice) {
$result .= $choice['message']['content'];
}
} catch (\Exception $e) {
throw new \Exception('GigaChat request error');
}
return $result;
}
public function get_models(): array {
return $this->send_request('models', [], false);
}
public function get_balance(): array {
return $this->send_request('balance', [], false);
}
function send_request(string $function, array $data, bool $is_post = true): array {
$client = $this->clientService->newClient();
$options = [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $this->get_access_token()
],
'verify' => false,
];
if (!empty($data)) {
$options['headers']['Content-Type'] = 'application/json';
$options['body'] = json_encode($data);
}
if ($is_post) {
$response = $client->post(self::METHOD_URL . $function, $options);
} else {
$response = $client->get(self::METHOD_URL . $function, $options);
}
$out = $response->getBody();
return json_decode($out, true) ?: [];
}
function get_access_token(): string {
$token = '';
$token_file = $this->get_access_token_file();
$token_data = json_decode($token_file->getContent(), true);
if (
empty($token_data['expires_at'])
|| (($token_data['expires_at'] / 1000) < time())
) {
$token = $this->refresh_token()['access_token'];
} else {
$token = $token_data['access_token'];
}
return $token;
}
public function get_access_token_file(): File {
$app_data_folder_name = $this->rootFolder->getAppDataDirectoryName();
$app_data_folder = $this->rootFolder->get($app_data_folder_name);
if (!$app_data_folder->nodeExists('/' . Application::APP_ID)) {
$app_data_folder->newFolder('/' . Application::APP_ID);
}
$app_folder = $app_data_folder->get('/' . Application::APP_ID);
if (!$app_folder->nodeExists(self::TOKEN_FILE)) {
$app_folder->newFile(self::TOKEN_FILE);
}
return $app_folder->get(self::TOKEN_FILE);
}
function refresh_token(): array
{
$client = $this->clientService->newClient();
$response = $client->post(self::TOKEN_URL, [
'body' => http_build_query(['scope' => $this->get_config_key(self::CONFIG_ACCESS_SCOPE)]),
'verify' => false,
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
'RqUID' => self::get_uuid4(),
'Authorization' => ' Basic ' . $this->get_config_key(self::CONFIG_ACCESS_KEY)
],
]);
$out = $response->getBody();
$token_data = json_decode($out, true) ?: [];
if (empty($token_data['access_token'])) {
throw new \Exception('Empty access_token ' . $out);
}
$token_file = $this->get_access_token_file();
$token_file->putContent($out);
return $token_data;
}
public static function get_uuid4(): string {
$data = random_bytes(16);
$data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
$data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
return vsprintf("%s%s-%s-%s-%s-%s%s%s", str_split(bin2hex($data), 4));
}
}