ai-ui/modules/site/controllers/AssistantController.php
ilhooq 6bfa38d7ae Correction bug lorsque body json dans requête GET
Cela fait planter l'autentification litellm
2025-04-01 21:26:09 +02:00

342 lines
9.0 KiB
PHP

<?php
namespace app\modules\site\controllers;
use Piko\User;
use Piko\HttpException;
use Monolog\Logger;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Utils;
use GuzzleHttp\Exception\RequestException;
use app\modules\site\models\Assistant;
use PDO;
class AssistantController extends \Piko\Controller
{
protected PDO $db;
protected User $user;
/**
* @var Logger
*/
private Logger $log;
protected function init(): void
{
$app = $this->module->getApplication();
$this->log = $app->getComponent(Logger::class);
assert($this->log instanceof Logger);
$this->user = $app->getComponent('Piko\User');
assert($this->user instanceof User);
$this->db = $app->getComponent('PDO');
assert($this->db instanceof PDO);
}
protected function getUserApiKey()
{
$identity = $this->user->getIdentity();
$apiKey = $identity->profil['api_key'] ?? null;
if ($apiKey === null) {
$this->log->error('API key not defined for user ID :' . $this->user->getId());
throw new HttpException(500, 'Internal error');
}
return $apiKey;
}
private function proxyRequest(string $method, string $endPoint, array $params = []): array
{
$client = new Client([
'base_uri' => getenv('PROXY_BASE_URL'),
]);
/*
$identity = $this->user->getIdentity();
$apiKey = $identity->profil['api_key'] ?? null;
if ($apiKey === null) {
$this->log->error('API key not defined for user ID :' . $this->user->getId());
throw new HttpException(500, 'Internal error');
}
*/
$apiKey = getenv('PROXY_MASTER_KEY');
$requestParams = [
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
]
];
if (!empty($params)) {
$requestParams['json'] = $params;
}
try {
$response = $client->request($method, $endPoint, $requestParams);
} catch (RequestException $e) {
$response = $e->getResponse();
$responseBody = (string) $response->getBody();
$contentType = $response->getHeader('Content-Type');
$errorCode = 500;
if ($contentType[0] == 'application/json') {
$data = json_decode($responseBody);
if (isset($data->error)) {
$responseBody = $data->error->message;
$errorCode = $data->error->code;
}
}
$this->log->error('Chat request error', ['request_body' => (string) $e->getRequest()->getBody()]);
$this->log->error('Chat response error', ['response_body' => $responseBody]);
throw new HttpException($errorCode, $responseBody);
}
$body = $response->getBody();
return [
'headers' => $response->getHeaders(),
'body' => (string) $body
];
}
private function getModels()
{
if (!isset($_SESSION['models'])) {
$models = [];
$response = $this->proxyRequest('GET', '/models');
if (isset($response['body'])) {
$body = json_decode($response['body'], true);
$data = $body['data'] ?? [];
foreach ($data as $obj) {
if (isset($obj['id'])) {
$models[] = $obj['id'];
}
}
}
$_SESSION['models'] = $models;
}
return $_SESSION['models'];
}
/**
* Stream de la réponse
*
* Pour désactiver l'output buffering:
* php -d output_buffering=0 -S localhost:8080 -t web
*
* @return void
*/
public function responseAction()
{
$identity = $this->user->getIdentity();
$client = new Client([
'base_uri' => getenv('PROXY_BASE_URL'),
'stream' => true,
]);
$headers = [
'Authorization' => 'Bearer ' . getenv('PROXY_KEY'),
'Content-Type' => 'application/json',
];
$data = json_decode((string) $this->request->getBody());
// bdump($data);
try {
$response = $client->request('POST', '/chat/completions', [
'headers' => $headers,
'stream' => true,
'json' => [
'model' => $data->model,
'messages' => $data->messages,
'stream' => true,
'temperature' => $data->temperature ?? 0,
'top_p' => $config->top_p ?? 0,
'user' => $identity->email
]
]);
} catch (RequestException $e) {
header("Content-Type: text/event-stream;charset=UTF-8");
$responseBody = (string) $e->getResponse()->getBody();
echo 'data: ' . str_replace("\n", '', $responseBody) . "\n\n";
$this->log->error('Chat request error', ['request_body' => (string) $e->getRequest()->getBody()]);
$this->log->error('Chat response error', ['response_body' => $responseBody]);
exit;
}
$body = $response->getBody();
$contentType = $response->getHeader('Content-Type');
if (count($contentType)) {
header("Content-Type:{$contentType[0]}");
}
$content = '';
while (!$body->eof()) {
$line = Utils::readLine($body);
if ($line != '[DONE]') {
$data = preg_replace('/^data:/', '', $line);
$data = json_decode($data, true);
if (isset($data['choices'][0]['delta']['content'])) {
$content .= $data['choices'][0]['delta']['content'];
}
}
echo $line;
flush();
}
exit;
}
public function indexAction()
{
// $assistants = Assistant::find($this->db, $this->user->getId(), '`title` ASC');
// $assistant = array_pop($assistants);
// $apiKey = $this->getUserApiKey();
// $assistant = Assistant::getDefaultUserAssistant($this->db, $this->user->getId());
return $this->render('index', [
// 'assistant' => $assistant,
'models' => $this->getModels(),
// 'apiKey' => $apiKey,
]);
}
public function listAction()
{
$assistants = Assistant::find($this->db, $this->user->getId(), '`title` ASC');
return $this->jsonResponse($assistants);
}
public function assistantsAction()
{
return $this->render('assistants', [
'assistants' => Assistant::find($this->db, $this->user->getId(), '`title` ASC')
]);
}
public function editAction($id = 0)
{
$model = new Assistant($this->db);
if ($id) {
$model->load($id);
}
$response = [];
$post = $this->request->getParsedBody();
if (!empty($post)) {
$model->bind($post);
$model->user_id = $this->user->getId();
try {
if ($model->isValid() && $model->save()) {
$response['status'] = 'success';
} else {
$response['status'] = 'error';
$response['error'] = array_pop($model->getErrors());
}
} catch (\Exception $e) {
exit ($e->getMessage());
}
}
$response['model'] = $model->toArray();
return $this->jsonResponse($response);
}
public function saveAction()
{
$model = new Assistant($this->db);
$data = json_decode((string) $this->request->getBody(), true);
if (isset($data['id'])) {
$model->load($data['id']);
unset($data['id']);
}
$response = [];
$model->bind($data);
$model->user_id = $this->user->getId();
try {
if ($model->isValid() && $model->save()) {
$response['status'] = 'success';
} else {
$response['status'] = 'error';
$response['errors'] = $model->getErrors();
}
} catch (\Exception $e) {
exit ($e->getMessage());
}
$response['assistant'] = $model->toArray();
return $this->jsonResponse($response);
}
public function setAsDefaultAction($id = 0)
{
$model = new Assistant($this->db);
if ($id) {
$model->load($id);
$model->default = 1;
$model->save();
return $this->jsonResponse(true);
}
return $this->jsonResponse(false);
}
public function deleteAction($id = 0)
{
$model = new Assistant($this->db);
if ($id) {
$model->load($id);
if ($model->delete()) {
return $this->jsonResponse(true);
}
}
return $this->jsonResponse(false);
}
}