-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Anupam Kumar <[email protected]>
- Loading branch information
Showing
20 changed files
with
2,108 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,362 @@ | ||
<?php | ||
|
||
namespace OCA\Assistant\Controller; | ||
|
||
use OCA\Assistant\AppInfo\Application; | ||
use OCA\Assistant\Db\ChattyLLM\Message; | ||
use OCA\Assistant\Db\ChattyLLM\MessageMapper; | ||
use OCA\Assistant\Db\ChattyLLM\Session; | ||
use OCA\Assistant\Db\ChattyLLM\SessionMapper; | ||
use OCA\Assistant\Service\AssistantService; | ||
use OCP\AppFramework\Controller; | ||
use OCP\AppFramework\Http; | ||
use OCP\AppFramework\Http\Attribute\NoAdminRequired; | ||
use OCP\AppFramework\Http\Attribute\OpenAPI; | ||
use OCP\AppFramework\Http\JSONResponse; | ||
use OCP\IConfig; | ||
use OCP\IL10N; | ||
use OCP\IRequest; | ||
use OCP\TextProcessing\FreePromptTaskType; | ||
use OCP\TextProcessing\Task as TextProcessingTask; | ||
use OCP\TextProcessing\IManager as ITextProcessingManager; | ||
use OCP\IUserManager; | ||
use Psr\Log\LoggerInterface; | ||
|
||
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] | ||
class ChattyLLMController extends Controller { | ||
|
||
public function __construct( | ||
string $appName, | ||
IRequest $request, | ||
private SessionMapper $sessionMapper, | ||
private MessageMapper $messageMapper, | ||
private IL10N $l10n, | ||
private LoggerInterface $logger, | ||
private AssistantService $assistantService, | ||
private ITextProcessingManager $textProcessingManager, | ||
private IConfig $config, | ||
private IUserManager $userManager, | ||
private ?string $userId, | ||
) { | ||
parent::__construct($appName, $request); | ||
} | ||
|
||
/** | ||
* @param string $content | ||
* @param int $timestamp | ||
* @param ?string $title | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function newSession(string $content, int $timestamp, ?string $title = null): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
$user = $this->userManager->get($this->userId); | ||
if ($user === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not found')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
$userInstructions = $this->config->getAppValue( | ||
Application::APP_ID, | ||
'chat_user_instructions', | ||
$this->l10n->t(Application::CHAT_USER_INSTRUCTIONS), | ||
); | ||
$userInstructions = str_replace('{user}', $user->getDisplayName(), $userInstructions); | ||
|
||
try { | ||
$session = new Session(); | ||
$session->setUserId($this->userId); | ||
$session->setTitle($title); | ||
Check failure on line 71 in lib/Controller/ChattyLLMController.php GitHub Actions / Psalm checkInvalidArgument
|
||
$session->setTimestamp($timestamp); | ||
$this->sessionMapper->insert($session); | ||
|
||
$systemMsg = new Message(); | ||
$systemMsg->setSessionId($session->getId()); | ||
$systemMsg->setRole($this->l10n->t('system')); | ||
$systemMsg->setContent($userInstructions); | ||
$systemMsg->setTimestamp($session->getTimestamp()); | ||
$this->messageMapper->insert($systemMsg); | ||
|
||
$humanMsg = new Message(); | ||
$humanMsg->setSessionId($session->getId()); | ||
$humanMsg->setRole($this->l10n->t('human')); | ||
$humanMsg->setContent($content); | ||
$humanMsg->setTimestamp($session->getTimestamp()); | ||
$this->messageMapper->insert($humanMsg); | ||
|
||
return new JSONResponse($session); | ||
} catch (\OCP\DB\Exception | \RuntimeException $e) { | ||
$this->logger->warning('Failed to create a chat session', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to create a chat session')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @param integer $sessionId | ||
* @param string $title | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function updateSession(int $sessionId, string $title): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$this->sessionMapper->updateSession($sessionId, $title); | ||
return new JSONResponse(); | ||
} catch (\OCP\DB\Exception | \RuntimeException $e) { | ||
$this->logger->warning('Failed to update the chat session', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to update the chat session')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @param integer $sessionId | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function deleteSession(int $sessionId): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$this->sessionMapper->deleteSession($sessionId); | ||
$this->messageMapper->deleteMessagesBySession($sessionId); | ||
return new JSONResponse(); | ||
} catch (\OCP\DB\Exception | \RuntimeException $e) { | ||
$this->logger->warning('Failed to delete the chat session', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to delete the chat session')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function getSessions(): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$sessions = $this->sessionMapper->getUserSessions($this->userId); | ||
return new JSONResponse($sessions); | ||
} catch (\OCP\DB\Exception $e) { | ||
$this->logger->warning('Failed to get chat sessions', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to get chat sessions')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @param int $sessionId | ||
* @param string $role | ||
* @param string $content | ||
* @param int $timestamp | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function newMessage(int $sessionId, string $role, string $content, int $timestamp): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$sessionExists = $this->sessionMapper->exists($sessionId); | ||
if (!$sessionExists) { | ||
return new JSONResponse(['error' => $this->l10n->t('Session not found')], Http::STATUS_NOT_FOUND); | ||
} | ||
|
||
$message = new Message(); | ||
$message->setSessionId($sessionId); | ||
$message->setRole($role); | ||
$message->setContent($content); | ||
$message->setTimestamp($timestamp); | ||
$this->messageMapper->insert($message); | ||
return new JSONResponse(); | ||
} catch (\OCP\DB\Exception $e) { | ||
$this->logger->warning('Failed to add a chat message', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to add a chat message')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @param int $sessionId | ||
* @param int $limit | ||
* @param int $cursor | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function getMessages(int $sessionId, int $limit = 20, int $cursor = 0): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$sessionExists = $this->sessionMapper->exists($sessionId); | ||
if (!$sessionExists) { | ||
return new JSONResponse(['error' => $this->l10n->t('Session not found')], Http::STATUS_NOT_FOUND); | ||
} | ||
|
||
$messages = $this->messageMapper->getMessages($sessionId, $cursor, $limit); | ||
if ($messages[0]->getRole() === $this->l10n->t('system')) { | ||
array_shift($messages); | ||
} | ||
|
||
return new JSONResponse($messages); | ||
} catch (\OCP\DB\Exception $e) { | ||
$this->logger->warning('Failed to get chat messages', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to get chat messages')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @param integer $messageId | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function deleteMessage(int $messageId): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$this->messageMapper->deleteMessageById($messageId); | ||
return new JSONResponse(); | ||
} catch (\OCP\DB\Exception | \RuntimeException $e) { | ||
$this->logger->warning('Failed to delete a chat message', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to delete a chat message')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @param integer $sessionId | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function generate(int $sessionId): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$sessionExists = $this->sessionMapper->exists($sessionId); | ||
if (!$sessionExists) { | ||
return new JSONResponse(['error' => $this->l10n->t('Session not found')], Http::STATUS_NOT_FOUND); | ||
} | ||
|
||
$stichedPrompt = | ||
$this->getStichedMessages($sessionId) | ||
. PHP_EOL | ||
. $this->l10n->t('assistant') . ': '; | ||
|
||
$result = $this->queryLLM($stichedPrompt); | ||
|
||
$message = new Message(); | ||
$message->setSessionId($sessionId); | ||
$message->setRole($this->l10n->t('assistant')); | ||
$message->setContent($result); | ||
$message->setTimestamp(time()); | ||
$this->messageMapper->insert($message); | ||
} catch (\OCP\DB\Exception $e) { | ||
$this->logger->warning('Failed to add a chat message into DB', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to add a chat message into DB')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
|
||
return new JSONResponse($message); | ||
} | ||
|
||
/** | ||
* @param integer $sessionId | ||
* @param integer $messageId | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function regenerate(int $sessionId, int $messageId): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$sessionExists = $this->sessionMapper->exists($sessionId); | ||
if (!$sessionExists) { | ||
return new JSONResponse(['error' => $this->l10n->t('Session not found')], Http::STATUS_NOT_FOUND); | ||
} | ||
|
||
$this->messageMapper->deleteMessagesSinceId($sessionId, $messageId); | ||
return $this->generate($sessionId); | ||
} catch (\OCP\DB\Exception $e) { | ||
$this->logger->warning('Failed to add a chat message into DB', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to add a chat message into DB')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* @param integer $sessionId | ||
* @return JSONResponse | ||
*/ | ||
#[NoAdminRequired] | ||
public function generateTitle(int $sessionId): JSONResponse { | ||
if ($this->userId === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not logged in')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
$user = $this->userManager->get($this->userId); | ||
if ($user === null) { | ||
return new JSONResponse(['error' => $this->l10n->t('User not found')], Http::STATUS_UNAUTHORIZED); | ||
} | ||
|
||
try { | ||
$sessionExists = $this->sessionMapper->exists($sessionId); | ||
if (!$sessionExists) { | ||
return new JSONResponse(['error' => $this->l10n->t('Session not found')], Http::STATUS_NOT_FOUND); | ||
} | ||
|
||
$userInstructions = $this->config->getAppValue( | ||
Application::APP_ID, | ||
'chat_user_instructions_title', | ||
$this->l10n->t(Application::CHAT_USER_INSTRUCTIONS_TITLE), | ||
); | ||
$userInstructions = str_replace('{user}', $user->getDisplayName(), $userInstructions); | ||
|
||
$stichedPrompt = $this->getStichedMessages($sessionId) | ||
. PHP_EOL . PHP_EOL | ||
. $userInstructions; | ||
|
||
$result = $this->queryLLM($stichedPrompt); | ||
$title = str_replace($userInstructions, '', $result); | ||
$title = str_replace('"', '', $title); | ||
$title = explode(PHP_EOL, $title)[0]; | ||
$title = trim($title); | ||
|
||
$this->sessionMapper->updateSession($sessionId, $title); | ||
|
||
return new JSONResponse(['result' => $title]); | ||
} catch (\OCP\DB\Exception $e) { | ||
$this->logger->warning('Failed to generate a title for the chat session', ['exception' => $e]); | ||
return new JSONResponse(['error' => $this->l10n->t('Failed to generate a title for the chat session')], Http::STATUS_INTERNAL_SERVER_ERROR); | ||
} | ||
} | ||
|
||
private function getStichedMessages(int $sessionId): string { | ||
$lastNMessages = intval($this->config->getAppValue(Application::APP_ID, 'chat_last_n_messages', 10)); | ||
Check failure on line 345 in lib/Controller/ChattyLLMController.php GitHub Actions / Psalm checkInvalidArgument
|
||
$messages = $this->messageMapper->getMessages($sessionId, 0, $lastNMessages); | ||
|
||
$stichedPrompt = implode(PHP_EOL, array_map(function ($message) { | ||
if ($message->getRole() === $this->l10n->t('system')) { | ||
return $message->getContent() . PHP_EOL; | ||
} | ||
return $message->getRole() . ': ' . $message->getContent(); | ||
}, $messages)); | ||
|
||
return $stichedPrompt; | ||
} | ||
|
||
private function queryLLM(string $content): string { | ||
$task = new TextProcessingTask(FreePromptTaskType::class, $content, Application::APP_ID, $this->userId); | ||
return trim($this->textProcessingManager->runTask($task)); | ||
} | ||
} |
Oops, something went wrong.