Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions lib/Db/ChattyLLM/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,22 @@ public function __construct(IDBConnection $db) {
/**
* @param integer $sessionId
* @param integer $n
* @param string|null $role
* @return Message
* @throws \OCP\DB\Exception
* @throws \RuntimeException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
*/
public function getFirstNMessages(int $sessionId, int $n = 1): Message {
public function getFirstNMessages(int $sessionId, int $n = 1, ?string $role = null): Message {
$qb = $this->db->getQueryBuilder();
$qb->select(Message::$columns)
->from($this->getTableName())
->where($qb->expr()->eq('session_id', $qb->createPositionalParameter($sessionId, IQueryBuilder::PARAM_INT)))
->where($qb->expr()->eq('session_id', $qb->createPositionalParameter($sessionId, IQueryBuilder::PARAM_INT)));
if ($role !== null) {
$qb->andWhere($qb->expr()->eq('role', $qb->createPositionalParameter($role, IQueryBuilder::PARAM_STR)));
}
$qb->orderBy('timestamp', 'ASC')
Comment on lines +42 to +45

@coderabbitai coderabbitai Bot Aug 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Order the filtered messages by ID.

getFirstNMessages() must return the first persisted message deterministically. timestamp can be caller-provided or shared by multiple messages, so ORDER BY timestamp ASC can select the wrong system message or produce database-dependent results. Use ORDER BY id ASC, consistent with the other message-history queries.

Proposed fix
-		$qb->orderBy('timestamp', 'ASC')
+		$qb->orderBy('id', 'ASC')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ($role !== null) {
$qb->andWhere($qb->expr()->eq('role', $qb->createPositionalParameter($role, IQueryBuilder::PARAM_STR)));
}
$qb->orderBy('timestamp', 'ASC')
if ($role !== null) {
$qb->andWhere($qb->expr()->eq('role', $qb->createPositionalParameter($role, IQueryBuilder::PARAM_STR)));
}
$qb->orderBy('id', 'ASC')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmh, Id is not guaratneed to be sequential

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Candidate schema and migration files =='
fd -t f . | rg -i '(^|/)(migration|schema|message).*\.(php|xml|json|sql)$' | head -n 200

printf '%s\n' '== assistant_chat_msgs declarations and inserts =='
rg -n -C 4 --glob '*.php' --glob '*.xml' --glob '*.sql' \
  '(assistant_chat_msgs|createTable\(.assistant_chat_msgs.|INSERT\s+INTO\s+.*assistant_chat_msgs|insert\(.assistant_chat_msgs.)' .

printf '%s\n' '== Message entity structure and persistence call sites =='
fd -t f 'Message.php' . -x sh -c 'echo "== $1 =="; ast-grep outline "$1" --items all --view expanded; sed -n "1,260p" "$1"' sh {} \
  | head -n 500
rg -n -C 4 --glob '*.php' '(insert\(\$message\)|insert\(\$[A-Za-z_]+Message\)|new Message\()' lib

Length of output: 14146


@marcelklehr, correct. The IDs do not need to be sequential.

ORDER BY id ASC selects the lowest persisted auto-increment ID. Gaps from deleted rows or rolled-back inserts do not change that order. The migration defines assistant_chat_msgs.id as an auto-increment column, and the insert path does not set the ID.

The issue with timestamp remains. Callers can provide the same timestamp for multiple messages, or a timestamp that does not match insertion order. I recommend ORDER BY id ASC for a deterministic persisted-message order.

You are interacting with an AI system.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add the id to the orderBy but I don't think we should remve the timestamp. We might soon move away from autoincrement IDs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcelklehr, agreed. timestamp should remain the primary ordering field.

Add id only as a deterministic tie-breaker:

$qb->orderBy('timestamp', 'ASC')
	->addOrderBy('id', 'ASC')

This does not require sequential IDs. It only requires that id has a stable order when two messages have the same timestamp. The primary behavior remains compatible with a future non-auto-increment ID scheme.


✏️ Learnings added
Learnt from: marcelklehr
Repo: nextcloud/assistant PR: 633
File: lib/Db/ChattyLLM/MessageMapper.php:42-45
Timestamp: 2026-08-26T12:00:59.687Z
Learning: In `lib/Db/ChattyLLM/MessageMapper.php`, `MessageMapper::getFirstNMessages()` must preserve `timestamp ASC` as the primary message order. Add `id ASC` only as a deterministic tie-breaker for equal timestamps. The application may replace auto-increment message IDs in the future.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

->setMaxResults($n);

return $this->findEntity($qb);
Expand Down
8 changes: 3 additions & 5 deletions lib/Service/ChatService.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,15 +415,13 @@ public function scheduleMessageGeneration(?string $userId, int $sessionId, int $
// classic chat
$systemPrompt = '';
try {
$firstMessage = $this->messageMapper->getFirstNMessages($sessionId, 1);
$firstMessage = $this->messageMapper->getFirstNMessages($sessionId, 1, Message::ROLE_SYSTEM);
$systemPrompt = $firstMessage->getContent();
} catch (DoesNotExistException $e) {
throw new NotFoundException($this->l10n->t('No message found in this session'), previous: $e);
$this->logger->info('No system message found in the session', ['exception' => $e, 'sessionId' => $sessionId]);
} catch (MultipleObjectsReturnedException|Exception $e) {
throw new InternalException(previous: $e);
}
if ($firstMessage->getRole() === Message::ROLE_SYSTEM) {
$systemPrompt = $firstMessage->getContent();
}
try {
$history = $this->getRawLastMessages($sessionId);
} catch (Exception|AppConfigTypeConflictException $e) {
Expand Down
Loading