diff --git a/appinfo/info.xml b/appinfo/info.xml
index 24a2ec6a..0d0227a3 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -62,7 +62,7 @@ Known providers:
More details on how to set this up in the [admin docs](https://docs.nextcloud.com/server/latest/admin_manual/ai/index.html)
]]>
- 2.4.0
+ 2.5.0
agpl
Julien Veyssier
Assistant
diff --git a/lib/Db/ChattyLLM/Message.php b/lib/Db/ChattyLLM/Message.php
index faad0a0f..d2b9dba4 100644
--- a/lib/Db/ChattyLLM/Message.php
+++ b/lib/Db/ChattyLLM/Message.php
@@ -23,6 +23,8 @@
* @method \void setTimestamp(int $timestamp)
* @method \int getOcpTaskId()
* @method \void setOcpTaskId(int $ocpTaskId)
+ * @method \string getSources()
+ * @method \void setSources(string $sources)
*/
class Message extends Entity implements \JsonSerializable {
/** @var int */
@@ -35,6 +37,8 @@ class Message extends Entity implements \JsonSerializable {
protected $timestamp;
/** @var int */
protected $ocpTaskId;
+ /** @var string */
+ protected $sources;
public static $columns = [
'id',
@@ -43,6 +47,7 @@ class Message extends Entity implements \JsonSerializable {
'content',
'timestamp',
'ocp_task_id',
+ 'sources',
];
public static $fields = [
'id',
@@ -51,6 +56,7 @@ class Message extends Entity implements \JsonSerializable {
'content',
'timestamp',
'ocpTaskId',
+ 'sources',
];
public function __construct() {
@@ -59,6 +65,7 @@ public function __construct() {
$this->addType('content', Types::STRING);
$this->addType('timestamp', Types::INTEGER);
$this->addType('ocp_task_id', Types::INTEGER);
+ $this->addType('sources', Types::STRING);
}
#[\ReturnTypeWillChange]
@@ -70,6 +77,7 @@ public function jsonSerialize() {
'content' => $this->content,
'timestamp' => $this->timestamp,
'ocp_task_id' => $this->ocpTaskId,
+ 'sources' => $this->sources,
];
}
}
diff --git a/lib/Listener/BeforeTemplateRenderedListener.php b/lib/Listener/BeforeTemplateRenderedListener.php
index 56c2db91..245c5623 100644
--- a/lib/Listener/BeforeTemplateRenderedListener.php
+++ b/lib/Listener/BeforeTemplateRenderedListener.php
@@ -10,6 +10,7 @@
namespace OCA\Assistant\Listener;
use OCA\Assistant\AppInfo\Application;
+use OCA\Assistant\Service\AssistantService;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
@@ -34,6 +35,7 @@ public function __construct(
private IAppConfig $appConfig,
private IInitialState $initialStateService,
private IEventDispatcher $eventDispatcher,
+ private AssistantService $assistantService,
private ?string $userId,
) {
}
@@ -63,6 +65,7 @@ public function handle(Event $event): void {
$this->initialStateService->provideInitialState('last-target-language', $lastTargetLanguage);
$indexingComplete = $this->appConfig->getValueInt('context_chat', 'last_indexed_time', 0) !== 0;
$this->initialStateService->provideInitialState('contextChatIndexingComplete', $indexingComplete);
+ $this->initialStateService->provideInitialState('contextAgentToolSources', $this->assistantService->informationSources);
}
if (class_exists(\OCA\Viewer\Event\LoadViewer::class)) {
$this->eventDispatcher->dispatchTyped(new \OCA\Viewer\Event\LoadViewer());
diff --git a/lib/Listener/ChattyLLMTaskListener.php b/lib/Listener/ChattyLLMTaskListener.php
index 4cb46b62..0f60a5c3 100644
--- a/lib/Listener/ChattyLLMTaskListener.php
+++ b/lib/Listener/ChattyLLMTaskListener.php
@@ -61,6 +61,8 @@ public function handle(Event $event): void {
$message->setRole('assistant');
$message->setContent(trim($task->getOutput()['output'] ?? ''));
$message->setTimestamp(time());
+ $sources = json_encode($task->getOutput()['sources'] ?? []);
+ $message->setSources($sources ? $sources : '[]');
try {
$this->messageMapper->insert($message);
} catch (\OCP\DB\Exception $e) {
diff --git a/lib/Migration/Version020500Date20250425125359.php b/lib/Migration/Version020500Date20250425125359.php
new file mode 100644
index 00000000..bf69c802
--- /dev/null
+++ b/lib/Migration/Version020500Date20250425125359.php
@@ -0,0 +1,43 @@
+hasTable('assistant_chat_msgs')) {
+ $table = $schema->getTable('assistant_chat_msgs');
+ if (!$table->hasColumn('sources')) {
+ $table->addColumn('sources', Types::TEXT, [
+ 'notnull' => true,
+ 'default' => '[]',
+ ]);
+ $schemaChanged = true;
+ }
+ }
+
+ return $schemaChanged ? $schema : null;
+ }
+}
diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php
index c9470dfd..4fbceec3 100644
--- a/lib/ResponseDefinitions.php
+++ b/lib/ResponseDefinitions.php
@@ -64,6 +64,7 @@
* content: string,
* timestamp: int,
* ocp_task_id: int,
+ * sources: string,
* }
*
* @psalm-type AssistantChatAgencyMessage = AssistantChatMessage&array{
diff --git a/lib/Service/AssistantService.php b/lib/Service/AssistantService.php
index aad0b0f3..233fc701 100644
--- a/lib/Service/AssistantService.php
+++ b/lib/Service/AssistantService.php
@@ -74,6 +74,8 @@ class AssistantService {
TextToTextTopics::ID => 10,
];
+ public array $informationSources;
+
public function __construct(
private ITaskProcessingManager $taskProcessingManager,
private TaskNotificationMapper $taskNotificationMapper,
@@ -86,6 +88,36 @@ public function __construct(
private IConfig $config,
private IShareManager $shareManager,
) {
+ $this->informationSources = [
+ 'ask_context_chat' => $this->l10n->t('Context Chat'),
+ 'transcribe_file' => $this->l10n->t('Assistant File Transcription'),
+ 'generate_document' => $this->l10n->t('Assistant Document Generation'),
+ 'list_calendars' => $this->l10n->t('Nextcloud Calendar'),
+ 'schedule_event' => $this->l10n->t('Nextcloud Calendar'),
+ 'find_free_time_slot_in_calendar' => $this->l10n->t('Nextcloud Calendar'),
+ 'add_task' => $this->l10n->t('Nextcloud Tasks'),
+ 'find_details_of_current_user' => $this->l10n->t('Nextcloud User Profile'),
+ 'list_decks' => $this->l10n->t('Nextcloud Deck'),
+ 'add_card' => $this->l10n->t('Nextcloud Deck'),
+ 'get_coordinates_for_address' => $this->l10n->t('OpenStreetMap'),
+ 'get_current_weather_for_coordinates' => $this->l10n->t('Norwegian Meteorological Institute Weather Forecast'),
+ 'get_public_transport_route_for_coordinates,' => $this->l10n->t('HERE Public Transport API'),
+ 'get_osm_route,' => $this->l10n->t('OpenStreetMap'),
+ 'get_osm_link,' => $this->l10n->t('OpenStreetMap'),
+ 'get_file_content' => $this->l10n->t('Nextcloud Files'),
+ 'get_folder_tree' => $this->l10n->t('Nextcloud Files'),
+ 'create_public_sharing_link' => $this->l10n->t('Nextcloud Files'),
+ 'send_email' => $this->l10n->t('Nextcloud Mail'),
+ 'get_mail_account_list' => $this->l10n->t('Nextcloud Mail'),
+ 'list_projects,' => $this->l10n->t('OpenProject'),
+ 'list_assignees,' => $this->l10n->t('OpenProject'),
+ 'create_work_package' => $this->l10n->t('OpenProject'),
+ 'list_talk_conversations' => $this->l10n->t('Nextcloud Talk'),
+ 'create_public_conversation' => $this->l10n->t('Nextcloud Talk'),
+ 'send_message_to_conversation' => $this->l10n->t('Nextcloud Talk'),
+ 'list_messages_in_conversation' => $this->l10n->t('Nextcloud Talk'),
+ 'duckduckgo_results_json' => $this->l10n->t('DuckDuckGo Web Search'),
+ ];
}
/**
diff --git a/openapi.json b/openapi.json
index 6b0d2626..3efbba35 100644
--- a/openapi.json
+++ b/openapi.json
@@ -49,7 +49,8 @@
"role",
"content",
"timestamp",
- "ocp_task_id"
+ "ocp_task_id",
+ "sources"
],
"properties": {
"id": {
@@ -73,6 +74,9 @@
"ocp_task_id": {
"type": "integer",
"format": "int64"
+ },
+ "sources": {
+ "type": "string"
}
}
},
diff --git a/src/components/ChattyLLM/ChattyLLMInputForm.vue b/src/components/ChattyLLM/ChattyLLMInputForm.vue
index e19c1f97..a392cfea 100644
--- a/src/components/ChattyLLM/ChattyLLMInputForm.vue
+++ b/src/components/ChattyLLM/ChattyLLMInputForm.vue
@@ -227,7 +227,7 @@ export default {
sessionIdToDelete: null,
chatContent: '',
sessions: null,
- // [{ id: number, session_id: number, role: string, content: string, timestamp: number }]
+ // [{ id: number, session_id: number, role: string, content: string, timestamp: number, sources:string }]
messages: [], // null when failed to fetch
messagesAxiosController: null, // for request cancellation
allMessagesLoaded: false,
diff --git a/src/components/ChattyLLM/ConversationBox.vue b/src/components/ChattyLLM/ConversationBox.vue
index d8436953..8d17b47c 100644
--- a/src/components/ChattyLLM/ConversationBox.vue
+++ b/src/components/ChattyLLM/ConversationBox.vue
@@ -28,6 +28,7 @@
:delete-loading="loading.messageDelete && message.id === deleteMessageId"
:regenerate-loading="loading.llmGeneration && message.id === regenerateFromId"
:new-message-loading="loading.newHumanMessage && idx === (messages.length - 1)"
+ :information-source-names="informationSourceNames"
@regenerate="regenerate(message.id)"
@delete="deleteMessage(message.id)" />
@@ -44,6 +45,8 @@ import LoadingPlaceholder from './LoadingPlaceholder.vue'
import Message from './Message.vue'
import NoSession from './NoSession.vue'
+import { loadState } from '@nextcloud/initial-state'
+
export default {
name: 'ConversationBox',
@@ -58,7 +61,7 @@ export default {
},
props: {
- // [{ id: number, session_id: number, role: string, content: string, timestamp: number }]
+ // [{ id: number, session_id: number, role: string, content: string, timestamp: number, sources: string }]
messages: {
type: Array,
default: null,
@@ -82,6 +85,7 @@ export default {
return {
regenerateFromId: null,
deleteMessageId: null,
+ informationSourceNames: loadState('assistant', 'contextAgentToolSources'),
}
},
diff --git a/src/components/ChattyLLM/Message.vue b/src/components/ChattyLLM/Message.vue
index a268c29d..dd706488 100644
--- a/src/components/ChattyLLM/Message.vue
+++ b/src/components/ChattyLLM/Message.vue
@@ -30,6 +30,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -48,8 +70,12 @@ import AssistantIcon from '../icons/AssistantIcon.vue'
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
+import NcPopover from '@nextcloud/vue/dist/Components/NcPopover.js'
+import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import { NcRichText } from '@nextcloud/vue/dist/Components/NcRichText.js'
+import InformationBox from 'vue-material-design-icons/InformationBox.vue'
+
import MessageActions from './MessageActions.vue'
import { getCurrentUser } from '@nextcloud/auth'
@@ -70,12 +96,15 @@ export default {
NcDateTime,
NcLoadingIcon,
NcRichText,
+ NcPopover,
+ NcButton,
+ InformationBox,
MessageActions,
},
props: {
- // { id: number, session_id: number, role: string, content: string, timestamp: number }
+ // { id: number, session_id: number, role: string, content: string, timestamp: number, sources: string }
message: {
type: Object,
required: true,
@@ -96,6 +125,10 @@ export default {
type: Boolean,
default: false,
},
+ informationSourceNames: {
+ type: Array,
+ default: null,
+ },
},
data: () => {
@@ -111,6 +144,14 @@ export default {
}
},
+ computed: {
+ parsedSources() {
+ let parsedSources = JSON.parse(this.message.sources)
+ parsedSources = parsedSources.map((source) => this.getSourceString(source))
+ return [...new Set(parsedSources)]
+ },
+ },
+
mounted() {
this.fetch()
},
@@ -138,6 +179,9 @@ export default {
})
}
},
+ getSourceString(source) {
+ return this.informationSourceNames[source] ? this.informationSourceNames[source] : source
+ },
},
}
@@ -155,6 +199,7 @@ export default {
&__header {
display: flex;
flex-direction: row;
+ flex-wrap: wrap;
align-items: center;
justify-content: space-between;
@@ -188,3 +233,16 @@ export default {
}
}
+
+