diff --git a/composer.json b/composer.json
index 7f54d4dd..3b7f9071 100644
--- a/composer.json
+++ b/composer.json
@@ -34,7 +34,7 @@
"nextcloud/ocp": "dev-master",
"nextcloud/openapi-extractor": "^1.0.0",
"phpunit/phpunit": "^9.5",
- "psalm/phar": "6.4.0"
+ "psalm/phar": "6.7"
},
"config": {
"sort-packages": true,
diff --git a/composer.lock b/composer.lock
index 4424dc23..d1b9e973 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "82ecee0e36d1b1a3fba5a93982fd6d62",
+ "content-hash": "9dbd950eb30116a6d75e71a01e759622",
"packages": [
{
"name": "erusev/parsedown",
@@ -1586,16 +1586,16 @@
},
{
"name": "psalm/phar",
- "version": "6.4.0",
+ "version": "6.7.0",
"source": {
"type": "git",
"url": "https://github.com/psalm/phar.git",
- "reference": "38771dadf1f2c64cadc3a81d12a73c23d040380f"
+ "reference": "8eb3a469fa888c862b8a382f1fe1671b4e989f06"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/psalm/phar/zipball/38771dadf1f2c64cadc3a81d12a73c23d040380f",
- "reference": "38771dadf1f2c64cadc3a81d12a73c23d040380f",
+ "url": "https://api.github.com/repos/psalm/phar/zipball/8eb3a469fa888c862b8a382f1fe1671b4e989f06",
+ "reference": "8eb3a469fa888c862b8a382f1fe1671b4e989f06",
"shasum": ""
},
"require": {
@@ -1615,9 +1615,9 @@
"description": "Composer-based Psalm Phar",
"support": {
"issues": "https://github.com/psalm/phar/issues",
- "source": "https://github.com/psalm/phar/tree/6.4.0"
+ "source": "https://github.com/psalm/phar/tree/6.7.0"
},
- "time": "2025-02-05T14:15:07+00:00"
+ "time": "2025-02-17T10:44:04+00:00"
},
{
"name": "psr/clock",
diff --git a/lib/Controller/ChattyLLMController.php b/lib/Controller/ChattyLLMController.php
index b3bda2ac..2af5b57f 100644
--- a/lib/Controller/ChattyLLMController.php
+++ b/lib/Controller/ChattyLLMController.php
@@ -500,11 +500,15 @@ public function generateForSession(int $sessionId, int $agencyConfirm = 0): JSON
$lastAttachments = $lastUserMessage->jsonSerialize()['attachments'];
$audioAttachment = $lastAttachments[0] ?? null;
+ // see https://github.com/vimeo/psalm/issues/7980
+ $isContextAgentAudioAvailable = false;
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\ContextAgentAudioInteraction')) {
+ $isContextAgentAudioAvailable = isset($this->taskProcessingManager->getAvailableTaskTypes()[\OCP\TaskProcessing\TaskTypes\ContextAgentAudioInteraction::ID]);
+ }
if ($audioAttachment !== null
&& isset($audioAttachment['type'])
&& $audioAttachment['type'] === 'Audio'
- && class_exists('OCP\\TaskProcessing\\TaskTypes\\ContextAgentAudioInteraction')
- && isset($this->taskProcessingManager->getAvailableTaskTypes()[\OCP\TaskProcessing\TaskTypes\ContextAgentAudioInteraction::ID])
+ && $isContextAgentAudioAvailable
) {
// audio agency
$fileId = $audioAttachment['file_id'];
@@ -536,11 +540,14 @@ public function generateForSession(int $sessionId, int $agencyConfirm = 0): JSON
$lastAttachments = $lastUserMessage->jsonSerialize()['attachments'];
$audioAttachment = $lastAttachments[0] ?? null;
+ $isAudioToAudioAvailable = false;
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\AudioToAudioChat')) {
+ $isAudioToAudioAvailable = isset($this->taskProcessingManager->getAvailableTaskTypes()[\OCP\TaskProcessing\TaskTypes\AudioToAudioChat::ID]);
+ }
if ($audioAttachment !== null
&& isset($audioAttachment['type'])
&& $audioAttachment['type'] === 'Audio'
- && class_exists('OCP\\TaskProcessing\\TaskTypes\\AudioToAudioChat')
- && isset($this->taskProcessingManager->getAvailableTaskTypes()[\OCP\TaskProcessing\TaskTypes\AudioToAudioChat::ID])
+ && $isAudioToAudioAvailable
) {
// for an audio chat task, let's try to get the remote audio IDs for all the previous audio messages
$history = $this->getAudioHistory($history);
@@ -1006,6 +1013,7 @@ private function scheduleAgencyTask(string $content, int $confirmation, string $
'confirmation' => $confirmation,
'conversation_token' => $conversationToken,
];
+ /** @psalm-suppress UndefinedClass */
$task = new Task(
\OCP\TaskProcessing\TaskTypes\ContextAgentInteraction::ID,
$taskInput,
@@ -1027,6 +1035,7 @@ private function scheduleAudioChatTask(
'system_prompt' => $systemPrompt,
'history' => $history,
];
+ /** @psalm-suppress UndefinedClass */
$task = new Task(
\OCP\TaskProcessing\TaskTypes\AudioToAudioChat::ID,
$input,
@@ -1048,6 +1057,7 @@ private function scheduleAgencyAudioTask(
'confirmation' => $confirmation,
'conversation_token' => $conversationToken,
];
+ /** @psalm-suppress UndefinedClass */
$task = new Task(
\OCP\TaskProcessing\TaskTypes\ContextAgentAudioInteraction::ID,
$taskInput,
diff --git a/lib/Listener/ChattyLLMTaskListener.php b/lib/Listener/ChattyLLMTaskListener.php
index 47f552ef..464a99e5 100644
--- a/lib/Listener/ChattyLLMTaskListener.php
+++ b/lib/Listener/ChattyLLMTaskListener.php
@@ -105,6 +105,8 @@ public function handle(Event $event): void {
} else {
$content = trim($taskOutput['output'] ?? '');
$message->setContent($content);
+ // the task is not an audio one, but we might still need to Tts the answer
+ // if it is a response to a ContextAgentInteraction confirmation that was asked about an audio message
$this->runTtsIfNeeded($sessionId, $message, $taskTypeId, $task->getUserId());
}
try {
@@ -135,7 +137,8 @@ public function handle(Event $event): void {
* @return void
*/
private function runTtsIfNeeded(int $sessionId, Message $message, string $taskTypeId, ?string $userId): void {
- if ($taskTypeId !== \OCP\TaskProcessing\TaskTypes\ContextAgentInteraction::ID) {
+ if (!class_exists('OCP\\TaskProcessing\\TaskTypes\\ContextAgentInteraction')
+ || $taskTypeId !== \OCP\TaskProcessing\TaskTypes\ContextAgentInteraction::ID) {
return;
}
// is the last non-empty user message an audio one?
@@ -157,6 +160,7 @@ private function runTtsIfNeeded(int $sessionId, Message $message, string $taskTy
*/
private function runTtsTask(Message $message, ?string $userId): void {
try {
+ /** @psalm-suppress UndefinedClass */
$task = new Task(
\OCP\TaskProcessing\TaskTypes\TextToSpeech::ID,
['input' => $message->getContent()],
diff --git a/lib/Service/AssistantService.php b/lib/Service/AssistantService.php
index acacaf2f..1998549f 100644
--- a/lib/Service/AssistantService.php
+++ b/lib/Service/AssistantService.php
@@ -214,12 +214,19 @@ public function cancelNotifyWhenReady(int $taskId, string $userId): void {
public function isAudioChatAvailable(): bool {
$availableTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes();
+ $ttsAvailable = false;
+ // see https://github.com/vimeo/psalm/issues/7980
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\TextToSpeech')) {
+ $ttsAvailable = array_key_exists(\OCP\TaskProcessing\TaskTypes\TextToSpeech::ID, $availableTaskTypes);
+ }
+ $audioToAudioAvailable = false;
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\AudioToAudioChat')) {
+ $audioToAudioAvailable = array_key_exists(\OCP\TaskProcessing\TaskTypes\AudioToAudioChat::ID, $availableTaskTypes);
+ }
// we have at least the simple audio chat task type and the 3 sub task types available
- return class_exists('OCP\\TaskProcessing\\TaskTypes\\AudioToAudioChat')
- && array_key_exists(\OCP\TaskProcessing\TaskTypes\AudioToAudioChat::ID, $availableTaskTypes)
+ return $audioToAudioAvailable
+ && $ttsAvailable
&& array_key_exists(AudioToText::ID, $availableTaskTypes)
- && class_exists('OCP\\TaskProcessing\\TaskTypes\\TextToSpeech')
- && array_key_exists(\OCP\TaskProcessing\TaskTypes\TextToSpeech::ID, $availableTaskTypes)
&& array_key_exists(TextToTextChat::ID, $availableTaskTypes);
}
@@ -291,21 +298,26 @@ public function getAvailableTaskTypes(): array {
if ($taskTypeArray['isInternal'] ?? false) {
continue;
}
- if (class_exists('OCP\\TaskProcessing\\TaskTypes\\TextToTextChatWithTools')
- && $typeId === \OCP\TaskProcessing\TaskTypes\TextToTextChatWithTools::ID) {
- continue;
+ // see https://github.com/vimeo/psalm/issues/7980
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\TextToTextChatWithTools')) {
+ if ($typeId === \OCP\TaskProcessing\TaskTypes\TextToTextChatWithTools::ID) {
+ continue;
+ }
}
- if (class_exists('OCP\\TaskProcessing\\TaskTypes\\ContextAgentInteraction')
- && $typeId === \OCP\TaskProcessing\TaskTypes\ContextAgentInteraction::ID) {
- continue;
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\ContextAgentInteraction')) {
+ if ($typeId === \OCP\TaskProcessing\TaskTypes\ContextAgentInteraction::ID) {
+ continue;
+ }
}
- if (class_exists('OCP\\TaskProcessing\\TaskTypes\\ContextAgentAudioInteraction')
- && $typeId === \OCP\TaskProcessing\TaskTypes\ContextAgentAudioInteraction::ID) {
- continue;
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\ContextAgentAudioInteraction')) {
+ if ($typeId === \OCP\TaskProcessing\TaskTypes\ContextAgentAudioInteraction::ID) {
+ continue;
+ }
}
- if (class_exists('OCP\\TaskProcessing\\TaskTypes\\AudioToAudioChat')
- && $typeId === \OCP\TaskProcessing\TaskTypes\AudioToAudioChat::ID) {
- continue;
+ if (class_exists('OCP\\TaskProcessing\\TaskTypes\\AudioToAudioChat')) {
+ if ($typeId === \OCP\TaskProcessing\TaskTypes\AudioToAudioChat::ID) {
+ continue;
+ }
}
}
if ($typeId === TextToTextChat::ID) {
diff --git a/lib/TaskProcessing/AudioToAudioChatProvider.php b/lib/TaskProcessing/AudioToAudioChatProvider.php
index b2d74a4c..336e72e3 100644
--- a/lib/TaskProcessing/AudioToAudioChatProvider.php
+++ b/lib/TaskProcessing/AudioToAudioChatProvider.php
@@ -41,6 +41,7 @@ public function getName(): string {
}
public function getTaskTypeId(): string {
+ /** @psalm-suppress UndefinedClass */
return AudioToAudioChat::ID;
}
@@ -134,6 +135,8 @@ public function process(?string $userId, array $input, callable $reportProgress)
// text to speech
try {
+ // this provider is not declared if TextToSpeech does not exist so we know it's fine
+ /** @psalm-suppress UndefinedClass */
$task = new Task(
TextToSpeech::ID,
['input' => $llmResult],
diff --git a/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php b/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php
index 5d57865a..fae2a81f 100644
--- a/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php
+++ b/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php
@@ -41,6 +41,7 @@ public function getName(): string {
}
public function getTaskTypeId(): string {
+ /** @psalm-suppress UndefinedClass */
return ContextAgentAudioInteraction::ID;
}
@@ -115,6 +116,7 @@ public function process(?string $userId, array $input, callable $reportProgress)
// context agent
try {
+ /** @psalm-suppress UndefinedClass */
$task = new Task(
ContextAgentInteraction::ID,
[
@@ -134,6 +136,7 @@ public function process(?string $userId, array $input, callable $reportProgress)
if ($agencyTaskOutput['output'] !== '') {
// text to speech
try {
+ /** @psalm-suppress UndefinedClass */
$task = new Task(
TextToSpeech::ID,
['input' => $agencyTaskOutput['output']],
diff --git a/psalm.xml b/psalm.xml
index 29986094..4eae0b07 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -9,6 +9,8 @@
findUnusedBaselineEntry="true"
findUnusedCode="false"
resolveFromConfigFile="true"
+ ensureOverrideAttribute="false"
+ strictBinaryOperands="false"
phpVersion="8.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
@@ -35,12 +37,6 @@
-
-
-
-
-
-