Add support for message history pagination, preserve scroll in rendering, and improve draft/message workflows.
This commit is contained in:
+19
-16
@@ -27,10 +27,10 @@ import { supportsReasoning } from "../../public/js/modelCapabilities.js";
|
||||
import {VISION_EVIDENCE_GUIDANCE} from "./visionAnalysis.js";
|
||||
|
||||
export function createChatService(repositories, modelService, config, webSearchProvider = new SearXNGSearchProvider(config), attachmentService = null, visionDebugService = null) {
|
||||
const formatChatMessage = row => {
|
||||
const formatChatMessage = (row, related = null) => {
|
||||
const message = formatBaseChatMessage(row);
|
||||
if (message?.role === "bot") {
|
||||
const versions = repositories.listMessageVersions(row.id);
|
||||
const versions = related?.versions ? related.versions.get(row.id) || [] : repositories.listMessageVersions(row.id);
|
||||
message.versionCount = versions.length || 1;
|
||||
message.versionIndex = versions.length ? Number(row.retry_active_index || 0) : 0;
|
||||
if (versions.length) message.responseVersions = versions.map(version => ({
|
||||
@@ -38,7 +38,7 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
sources: parseSources(version.sources), modelName: version.model_name, createdAt: version.created_at
|
||||
}));
|
||||
}
|
||||
const attachments = row && attachmentService?.list(row.id);
|
||||
const attachments = row && (related?.attachments ? related.attachments.get(row.id) : attachmentService?.list(row.id));
|
||||
return message && attachments?.length ? { ...message, attachments } : message;
|
||||
};
|
||||
const chatGenerationQueues = new Map();
|
||||
@@ -198,6 +198,9 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
|
||||
const latestSummarized = summarizable[summarizable.length - 1];
|
||||
if (!latestSummarized || !summary) return;
|
||||
const current = repositories.getChat(chatId, user);
|
||||
// A manual correction or branch switch during generation wins over the stale summary.
|
||||
if (!current || current.context_summary !== session.context_summary || current.context_summary_message_id !== session.context_summary_message_id) return;
|
||||
repositories.updateChatContextSummary(chatId, user, clampSummary(summary), latestSummarized.id);
|
||||
};
|
||||
|
||||
@@ -573,18 +576,17 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
}
|
||||
});
|
||||
|
||||
const listActiveBranchMessages = (chatId) => repositories.listActiveBranchMessages(chatId).map((row) => {
|
||||
const message = formatChatMessage(row);
|
||||
const siblings = repositories.listSiblingChatMessages(chatId, row.role, row.parent_message_id);
|
||||
const siblingIndex = siblings.findIndex((sibling) => sibling.id === row.id);
|
||||
return {
|
||||
...message,
|
||||
siblingIndex,
|
||||
siblingCount: siblings.length,
|
||||
previousSiblingId: siblingIndex > 0 ? siblings[siblingIndex - 1].id : null,
|
||||
nextSiblingId: siblingIndex >= 0 && siblingIndex < siblings.length - 1 ? siblings[siblingIndex + 1].id : null
|
||||
};
|
||||
});
|
||||
const formatHistory = (chatId, rows) => {
|
||||
const metadata = repositories.getHistoryMetadata(chatId, rows.map(row => row.id));
|
||||
return rows.map(row => ({...formatChatMessage(row, metadata), ...metadata.siblings.get(row.id)}));
|
||||
};
|
||||
const listActiveBranchMessages = chatId => formatHistory(chatId, repositories.listActiveBranchMessages(chatId));
|
||||
const getHistoryPage = (chatId, options) => {
|
||||
const result = repositories.getActiveBranchPage(chatId, options);
|
||||
if (result.error) return result;
|
||||
const {rows, ...details} = result;
|
||||
return {...details, messages: formatHistory(chatId, rows)};
|
||||
};
|
||||
|
||||
const cloneChatThroughMessage = ({ user, sourceChat, messageId = null }) => {
|
||||
if (repositories.countChats(user) >= config.chatLimit) {
|
||||
@@ -779,9 +781,10 @@ export function createChatService(repositories, modelService, config, webSearchP
|
||||
const leaf = includeDescendants ? repositories.getLatestDescendantMessage(chatId, messageId) : message;
|
||||
repositories.updateChatActiveLeaf(chatId, user, leaf?.id || messageId);
|
||||
repositories.updateChatContextSummary(chatId, user, null, 0);
|
||||
return { messages: listActiveBranchMessages(chatId) };
|
||||
return getHistoryPage(chatId);
|
||||
},
|
||||
listActiveBranchMessages,
|
||||
getHistoryPage,
|
||||
stopGeneration({ user, chatId }) {
|
||||
if (!repositories.getChat(chatId, user)) return { error: "Chat not found", status: 404 };
|
||||
const controller = activeGenerations.get(chatId);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// bcrypt uses the first 72 UTF-8 bytes. Apply this only when creating a new hash:
|
||||
// existing credentials must remain usable for login and current-password checks.
|
||||
export function newPasswordError(password) {
|
||||
return Buffer.byteLength(password, "utf8") > 72
|
||||
? "Password must be at most 72 UTF-8 bytes (some characters use multiple bytes)"
|
||||
: null;
|
||||
}
|
||||
Reference in New Issue
Block a user