@@ -399,6 +399,13 @@ public function scheduleMessageGeneration(?string $userId, int $sessionId, int $
399399 // audio agency
400400 $ fileId = $ audioAttachment ['file_id ' ];
401401 $ taskId = $ this ->scheduleAgencyAudioTask ($ userId , $ fileId , $ agencyConfirm , $ lastConversationToken , $ sessionId , $ lastUserMessage ->getId ());
402+ } elseif ($ this ->isMultimodalContextAgentAvailable ()) {
403+ // multimodal agency
404+ $ prompt = $ lastUserMessage ->getContent ();
405+ $ inputAttachments = array_map (static function (array $ attachment ) {
406+ return $ attachment ['file_id ' ];
407+ }, $ lastAttachments );
408+ $ taskId = $ this ->scheduleAgencyMultimodalTask ($ userId , $ prompt , $ agencyConfirm , $ lastConversationToken , $ sessionId , $ inputAttachments );
402409 } else {
403410 // classic agency
404411 $ prompt = $ lastUserMessage ->getContent ();
@@ -446,14 +453,40 @@ public function scheduleMessageGeneration(?string $userId, int $sessionId, int $
446453 $ fileId = $ audioAttachment ['file_id ' ];
447454 $ taskId = $ this ->scheduleAudioChatTask ($ userId , $ fileId , $ systemPrompt , $ history , $ sessionId , $ lastUserMessage ->getId ());
448455 } else {
449- // for a text chat task, let's only use text in the history
450- $ history = array_map (static function (Message $ message ) {
451- return json_encode ([
452- 'role ' => $ message ->getRole (),
453- 'content ' => $ message ->getContent (),
454- ]);
455- }, $ history );
456- $ taskId = $ this ->scheduleLLMChatTask ($ userId , $ lastUserMessage ->getContent (), $ systemPrompt , $ history , $ sessionId );
456+ if ($ this ->isMultimodalChatAvailable ()) {
457+ // for a multimodal chat also attachments need to be added to the history
458+ $ historyMessages = array_map (static function (Message $ message ) {
459+ $ attachments = $ message ->jsonSerialize ()['attachments ' ];
460+ $ content = array_map (static function (array $ attachment ) {
461+ $ newAttachment = ['type ' => 'file ' , 'file_id ' => $ attachment ['file_id ' ]];
462+ if (isset ($ attachment ['ocp_task_id ' ])) {
463+ $ newAttachment ['ocp_task_id ' ] = $ attachment ['ocp_task_id ' ];
464+ }
465+ return $ newAttachment ;
466+ }, $ attachments );
467+ $ content [] = [
468+ 'type ' => 'text ' ,
469+ 'text ' => $ message ->getContent (),
470+ ];
471+ return json_encode ([
472+ 'role ' => $ message ->getRole (),
473+ 'content ' => $ content ,
474+ ]);
475+ }, $ history );
476+ $ lastAttachments = array_map (static function (array $ attachment ) {
477+ return $ attachment ['file_id ' ];
478+ }, $ lastAttachments );
479+ $ taskId = $ this ->scheduleMultimodalChatTask ($ userId , $ lastUserMessage ->getContent (), $ systemPrompt , $ historyMessages , $ sessionId , $ lastAttachments );
480+ } else {
481+ // for a text chat task only use text in the history
482+ $ historyMessages = array_map (static function (Message $ message ) {
483+ return json_encode ([
484+ 'role ' => $ message ->getRole (),
485+ 'content ' => $ message ->getContent (),
486+ ]);
487+ }, $ history );
488+ $ taskId = $ this ->scheduleLLMChatTask ($ userId , $ lastUserMessage ->getContent (), $ systemPrompt , $ historyMessages , $ sessionId );
489+ }
457490 }
458491 }
459492 return $ taskId ;
@@ -567,6 +600,20 @@ public function isContextAgentAudioAvailable(): bool {
567600 return in_array (\OCP \TaskProcessing \TaskTypes \ContextAgentAudioInteraction::ID , $ this ->taskProcessingManager ->getAvailableTaskTypeIds ());
568601 }
569602
603+ public function isMultimodalChatAvailable (): bool {
604+ if (!class_exists ('OCP \\TaskProcessing \\TaskTypes \\MultimodalChatWithTools ' )) {
605+ return false ;
606+ }
607+ return in_array (\OCP \TaskProcessing \TaskTypes \MultimodalChatWithTools::ID , $ this ->taskProcessingManager ->getAvailableTaskTypeIds ());
608+ }
609+
610+ public function isMultimodalContextAgentAvailable (): bool {
611+ if (!class_exists ('OCP \\TaskProcessing \\TaskTypes \\MultimodalContextAgentInteraction ' )) {
612+ return false ;
613+ }
614+ return in_array (\OCP \TaskProcessing \TaskTypes \MultimodalContextAgentInteraction::ID , $ this ->taskProcessingManager ->getAvailableTaskTypeIds ());
615+ }
616+
570617 private function getAudioHistory (array $ history ): array {
571618 // history is a list of JSON strings
572619 // The content is the remote audio ID (or the transcription as fallback)
@@ -682,6 +729,49 @@ private function scheduleLLMChatTask(
682729 return $ task ->getId () ?? 0 ;
683730 }
684731
732+ /**
733+ * Schedule a Multimodal Chat task
734+ *
735+ * @throws BadRequestException
736+ * @throws InternalException
737+ */
738+ private function scheduleMultimodalChatTask (
739+ ?string $ userId ,
740+ string $ content ,
741+ string $ systemPrompt ,
742+ array $ history ,
743+ int $ sessionId ,
744+ array $ attachmentsHistory ,
745+ ): int {
746+ $ customId = 'chatty-llm: ' . $ sessionId ;
747+ $ this ->checkIfSessionIsThinking ($ userId , $ customId );
748+ $ input = [
749+ 'input ' => $ content ,
750+ 'system_prompt ' => $ systemPrompt ,
751+ 'history ' => $ history ,
752+ 'input_attachments ' => $ attachmentsHistory ,
753+ 'tools ' => '[] ' , // Empty tools as there is not a non tools version
754+ 'tool_message ' => '' ,
755+ ];
756+ /** @psalm-suppress UndefinedClass */
757+ $ task = new Task (\OCP \TaskProcessing \TaskTypes \MultimodalChatWithTools::ID , $ input , Application::APP_ID . ':chatty-llm ' , $ userId , $ customId );
758+ /** @psalm-suppress UndefinedMethod */
759+ $ task ->setPreferStreaming (true );
760+ try {
761+ $ this ->taskProcessingManager ->scheduleTask ($ task );
762+ } catch (PreConditionNotMetException $ e ) {
763+ throw new BadRequestException ('pre_condition_not_met ' , previous: $ e );
764+ } catch (\OCP \TaskProcessing \Exception \UnauthorizedException $ e ) {
765+ throw new BadRequestException ('unauthorized ' , previous: $ e );
766+ } catch (ValidationException $ e ) {
767+ throw new BadRequestException ('validation_failed ' , previous: $ e );
768+ } catch (\OCP \TaskProcessing \Exception \Exception $ e ) {
769+ $ this ->logger ->error ($ e ->getMessage (), ['exception ' => $ e ]);
770+ throw new InternalException (previous: $ e );
771+ }
772+ return $ task ->getId () ?? 0 ;
773+ }
774+
685775 /**
686776 * Schedule an agency chat task
687777 *
@@ -731,6 +821,55 @@ private function scheduleAgencyTask(
731821 return $ task ->getId () ?? 0 ;
732822 }
733823
824+ /**
825+ * Schedule a multimodal agency chat task
826+ *
827+ * @param list<int> $inputAttachments
828+ * @throws BadRequestException
829+ * @throws InternalException
830+ */
831+ private function scheduleAgencyMultimodalTask (
832+ ?string $ userId ,
833+ string $ content ,
834+ int $ confirmation ,
835+ string $ conversationToken ,
836+ int $ sessionId ,
837+ array $ inputAttachments ,
838+ ): int {
839+ $ customId = 'chatty-llm: ' . $ sessionId ;
840+ $ this ->checkIfSessionIsThinking ($ userId , $ customId );
841+ $ taskInput = [
842+ 'input ' => $ content ,
843+ 'input_attachments ' => $ inputAttachments ,
844+ 'confirmation ' => $ confirmation ,
845+ 'conversation_token ' => $ conversationToken ,
846+ ];
847+ $ taskInput ['memories ' ] = $ this ->sessionSummaryService ->getMemories ($ userId );
848+ /** @psalm-suppress UndefinedClass */
849+ $ task = new Task (
850+ \OCP \TaskProcessing \TaskTypes \MultimodalContextAgentInteraction::ID ,
851+ $ taskInput ,
852+ Application::APP_ID . ':chatty-llm ' ,
853+ $ userId ,
854+ $ customId
855+ );
856+ /** @psalm-suppress UndefinedMethod */
857+ $ task ->setPreferStreaming (true );
858+ try {
859+ $ this ->taskProcessingManager ->scheduleTask ($ task );
860+ } catch (PreConditionNotMetException $ e ) {
861+ throw new BadRequestException ('pre_condition_not_met ' , previous: $ e );
862+ } catch (\OCP \TaskProcessing \Exception \UnauthorizedException $ e ) {
863+ throw new BadRequestException ('unauthorized ' , previous: $ e );
864+ } catch (ValidationException $ e ) {
865+ throw new BadRequestException ('validation_failed ' , previous: $ e );
866+ } catch (\OCP \TaskProcessing \Exception \Exception $ e ) {
867+ $ this ->logger ->error ($ e ->getMessage (), ['exception ' => $ e ]);
868+ throw new InternalException (previous: $ e );
869+ }
870+ return $ task ->getId () ?? 0 ;
871+ }
872+
734873 /**
735874 * Schedule an audio chat task
736875 * @throws BadRequestException
0 commit comments