| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- <?php defined('BX_DOL') or die('hack attempt');
- /**
- * Copyright (c) UNA, Inc - https://una.io
- * MIT License - https://opensource.org/licenses/MIT
- *
- * @defgroup AIProxy AI Proxy
- * @ingroup UnaModules
- *
- * @{
- */
- class BxAiProxyModule extends BxDolModule
- {
- function __construct($aModule)
- {
- parent::__construct($aModule);
- }
- public function serviceGetModelOptions()
- {
- $aResult = [
- ['key' => '', 'value' => _t('_Select_one')],
- ];
- $aModels = (new BxDolAiQuery())->getModelsBy([
- 'sample' => 'all_pairs',
- 'active' => 1,
- 'capabilities' => ['chatvlm', 'chatllm'],
- 'type_not' => 'una-proxy',
- ]);
- if (!is_array($aModels))
- return $aResult;
- foreach ($aModels as $iId => $sTitle)
- $aResult[] = ['key' => (int)$iId, 'value' => (string)$sTitle];
- return $aResult;
- }
- public function serviceGetKnowledgeOptions()
- {
- $aResult = [
- ['key' => '', 'value' => _t('_Select_one')],
- ];
- $aStores = $this->_oDb->getActiveKnowledgeStores();
- foreach ($aStores as $iId => $sTitle)
- $aResult[] = ['key' => (int)$iId, 'value' => (string)$sTitle];
- return $aResult;
- }
- public function completions(): void
- {
- bx_import('Completion', $this->_aModule);
- $aBody = $this->authenticatedBody();
- $bStarted = false;
- try {
- $iId = (int)getParam('bx_ai_proxy_model');
- $this->assertChatModel($iId);
- $fFactory = static function (int $iModelId) {
- return BxDolAIModelFactory::getModelInstance($iModelId);
- };
- $oCompletion = new BxAiProxyCompletion((string)getParam('bx_ai_proxy_prompt'));
- if (!empty($aBody['stream'])) {
- $oCompletion->run($aBody, $iId, $fFactory, function (string $sEvent) use (&$bStarted) {
- if (!$bStarted) {
- header('Content-Type: text/event-stream; charset=utf-8');
- header('Cache-Control: no-cache');
- header('X-Accel-Buffering: no');
- $bStarted = true;
- }
- echo $sEvent;
- if (ob_get_level())
- ob_flush();
- flush();
- });
- return;
- }
- $sJson = $oCompletion->run($aBody, $iId, $fFactory);
- header('Content-Type: application/json; charset=utf-8');
- echo (string)$sJson;
- }
- catch (BxAiProxyException $oException) {
- if ($bStarted || headers_sent())
- return;
- $sType = 'server_error';
- if ($oException->httpCode() === 400)
- $sType = 'invalid_request_error';
- elseif ($oException->httpCode() === 401)
- $sType = 'authentication_error';
- $this->jsonError($oException->httpCode(), $oException->getMessage(), $sType);
- }
- catch (Throwable $oException) {
- bx_log('sys_agents', 'AI proxy failed: ' . $oException->getMessage(), BX_LOG_ERR);
- if ($bStarted || headers_sent())
- return;
- $this->jsonError(503, 'The proxy model is not available', 'server_error');
- }
- }
- public function knowledge(): void
- {
- bx_import('Completion', $this->_aModule);
- bx_import('Knowledge', $this->_aModule);
- $aBody = $this->authenticatedBody();
- try {
- $iId = (int)getParam('bx_ai_proxy_knowledge');
- $aResult = (new BxAiProxyKnowledge())->run(
- $aBody,
- $iId,
- static function (int $iStoreId) {
- return BxDolAiQuery::getVectorStoreObject($iStoreId);
- },
- static function (int $iModelId) {
- return BxDolAIModelFactory::getModelInstance($iModelId);
- },
- static function (int $iStoreId) {
- return BxDolAIVectorStoreFactory::getVectorStoreInstance($iStoreId);
- }
- );
- header('Content-Type: application/json; charset=utf-8');
- echo json_encode($aResult, JSON_UNESCAPED_UNICODE);
- }
- catch (BxAiProxyException $oException) {
- $sType = 'server_error';
- if ($oException->httpCode() === 400)
- $sType = 'invalid_request_error';
- elseif ($oException->httpCode() === 401)
- $sType = 'authentication_error';
- $this->jsonError($oException->httpCode(), $oException->getMessage(), $sType);
- }
- catch (Throwable $oException) {
- bx_log('sys_agents', 'AI proxy knowledge failed: ' . $oException->getMessage(), BX_LOG_ERR);
- $this->jsonError(503, 'The proxy knowledge is not available', 'server_error');
- }
- }
- /**
- * @return array<string, mixed>
- */
- protected function authenticatedBody(): array
- {
- bx_import('Auth', $this->_aModule);
- $sBody = file_get_contents('php://input');
- if ($sBody === false)
- $sBody = '';
- $aAuth = (new BxAiProxyAuth())->authenticateRequest(
- $this->requestHeader('X-Una-Key'),
- $this->requestHeader('X-Una-Sign'),
- $sBody
- );
- if (empty($aAuth['ok']))
- $this->jsonError(401, 'Unauthorized', 'authentication_error');
- if(($sMl = 'bx_market_api') && ($sMd = 'is_operator_chat') && bx_is_srv($sMl, $sMd)) {
- $sKey = getParam('sys_oauth_key');
- if($sKey && !bx_srv($sMl, $sMd, [$sKey]))
- $this->jsonError(402, 'Payment Required', 'payment_required');
- }
- $aBody = json_decode($sBody, true);
- if (!is_array($aBody))
- $this->jsonError(400, 'Bad request', 'invalid_request_error');
- return $aBody;
- }
- public function requestHeader(string $sName): string
- {
- $sServer = 'HTTP_' . strtoupper(str_replace('-', '_', $sName));
- foreach ([$sServer, 'REDIRECT_' . $sServer] as $sKey) {
- if (isset($_SERVER[$sKey]) && (string)$_SERVER[$sKey] !== '')
- return trim((string)$_SERVER[$sKey]);
- }
- if (function_exists('getallheaders')) {
- $aHeaders = getallheaders();
- if (is_array($aHeaders)) {
- foreach ($aHeaders as $sHeader => $sValue) {
- if (strcasecmp((string)$sHeader, $sName) === 0)
- return trim((string)$sValue);
- }
- }
- }
- return '';
- }
- protected function assertChatModel(int $iId): void
- {
- bx_import('Completion', $this->_aModule);
- if ($iId <= 0)
- throw new BxAiProxyException(503, 'The proxy model is not available');
- $aModel = BxDolAiQuery::getModelObject($iId);
- if (!is_array($aModel) || empty($aModel['active']))
- throw new BxAiProxyException(503, 'The proxy model is not available');
- if (!in_array((string)($aModel['capabilities'] ?? ''), ['chatllm', 'chatvlm'], true))
- throw new BxAiProxyException(503, 'The proxy model is not available');
- if ((string)($aModel['type'] ?? '') === 'una-proxy')
- throw new BxAiProxyException(503, 'The proxy model is not available');
- }
- protected function jsonError(int $iCode, string $sMessage, string $sType): void
- {
- http_response_code($iCode);
- header('Content-Type: application/json; charset=utf-8');
- echo json_encode([
- 'error' => [
- 'message' => $sMessage,
- 'type' => $sType,
- ],
- ], JSON_UNESCAPED_UNICODE);
- exit;
- }
- }
- /** @} */
|