runLLMQuery($userId, $presentationText); $ooxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', 'ooxml') === 'ooxml'; $format = $ooxml ? 'pptx' : 'odp'; try { [$presentationStyle, $parsedStructure] = $this->parseModelJSON($rawModelOutput); } catch (\JsonException) { throw new RuntimeException('LLM generated faulty JSON data'); } $emptyPresentation = $this->getPresentationTemplate($presentationStyle); try { $transformedPresentation = $this->remoteService->transformDocumentStructure( 'presentation.' . $format, $emptyPresentation, $parsedStructure, $format ); return $transformedPresentation; } catch (\Exception) { throw new RuntimeException('Unable to apply transformations to presentation file'); } } /** * Parses the JSON output from the LLM into * JSON that Collabora expects * * @param string $jsonString * @return array */ private function parseModelJSON(string $jsonString): array { $jsonString = trim($jsonString, "` \n\r\t\v\0"); $modelJSON = json_decode( $jsonString, associative: true, flags: JSON_THROW_ON_ERROR ); $layoutTypes = array_column(LayoutType::cases(), 'value'); $presentation = new Presentation(); foreach ($modelJSON as $index => $slideJSON) { if ($slideJSON['presentationStyle']) { $presentation->setStyle($slideJSON['presentationStyle']); continue; } $validLayout = array_key_exists($slideJSON['layout'], $layoutTypes); if (!$validLayout) { continue; } $slideLayout = LayoutType::from($layoutTypes[$slideJSON['layout']]); $slide = match ($slideLayout) { LayoutType::Title => new TitleSlide($index, $slideJSON['title'], $slideJSON['subtitle']), LayoutType::TitleContent => new TitleContentSlide($index, $slideJSON['title'], $slideJSON['content']), default => null, }; if (is_null($slide)) { continue; } $presentation->addSlide($slide); } return [$presentation->getStyle(), $presentation->getSlideCommands()]; } /** * Creates a presentation file in memory * * @param string $name * @return resource */ private function getPresentationTemplate(string $name = '') { $emptyPresentationContent = $this->templateManager->getAITemplate($name); $memoryStream = fopen('php://memory', 'r+'); if (!$memoryStream) { throw new RuntimeException('Unable to open file stream'); } fwrite($memoryStream, $emptyPresentationContent); rewind($memoryStream); return $memoryStream; } private function runLLMQuery(?string $userId, string $presentationText) { $prompt = self::PROMPT; $task = new Task( TextToText::ID, ['input' => $prompt . "\n\n" . $presentationText], Application::APPNAME, $userId ); try { $task = $this->taskProcessingManager->runTask($task); } catch (PreConditionNotMetException|UnauthorizedException|ValidationException|Exception $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } $taskOutput = $task->getOutput(); if ($taskOutput === null) { throw new RuntimeException('Task with id ' . $task->getId() . ' does not have any output'); } /** @var string $taskOutputString */ $taskOutputString = $taskOutput['output']; return $taskOutputString; } }