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