f7cloud_client/apps/gigachat_integration/lib/Service/GigaChatService.php
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
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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));
}
}