From a362963017d91dda6dfe0bc80572e46e5fcd231c Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 27 Aug 2025 13:48:48 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20String=20Interpolation=20?= =?UTF-8?q?in=20Messages=20Endpoint=20from=20#9155=20(#9312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: move buildTree function for message hierarchy to data provider * refactor: consolidate buildTree import from utils to data provider * fix: correct string interpolation in messages function, which caused message search requests to fail --- api/models/convoStructure.spec.js | 40 +-------------- client/src/components/Chat/ChatView.tsx | 4 +- client/src/components/Share/ShareView.tsx | 2 +- .../Conversations/useExportConversation.ts | 3 +- client/src/routes/Search.tsx | 2 +- client/src/utils/buildTree.ts | 47 +---------------- client/src/utils/index.ts | 1 - package-lock.json | 2 +- packages/data-provider/package.json | 2 +- packages/data-provider/src/api-endpoints.ts | 2 +- packages/data-provider/src/index.ts | 2 + packages/data-provider/src/messages.ts | 50 +++++++++++++++++++ 12 files changed, 63 insertions(+), 94 deletions(-) create mode 100644 packages/data-provider/src/messages.ts diff --git a/api/models/convoStructure.spec.js b/api/models/convoStructure.spec.js index 33bf0c9b2..440f21cb0 100644 --- a/api/models/convoStructure.spec.js +++ b/api/models/convoStructure.spec.js @@ -1,47 +1,9 @@ const mongoose = require('mongoose'); +const { buildTree } = require('librechat-data-provider'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { getMessages, bulkSaveMessages } = require('./Message'); const { Message } = require('~/db/models'); -// Original version of buildTree function -function buildTree({ messages, fileMap }) { - if (messages === null) { - return null; - } - - const messageMap = {}; - const rootMessages = []; - const childrenCount = {}; - - messages.forEach((message) => { - const parentId = message.parentMessageId ?? ''; - childrenCount[parentId] = (childrenCount[parentId] || 0) + 1; - - const extendedMessage = { - ...message, - children: [], - depth: 0, - siblingIndex: childrenCount[parentId] - 1, - }; - - if (message.files && fileMap) { - extendedMessage.files = message.files.map((file) => fileMap[file.file_id ?? ''] ?? file); - } - - messageMap[message.messageId] = extendedMessage; - - const parentMessage = messageMap[parentId]; - if (parentMessage) { - parentMessage.children.push(extendedMessage); - extendedMessage.depth = parentMessage.depth + 1; - } else { - rootMessages.push(extendedMessage); - } - }); - - return rootMessages; -} - let mongod; beforeAll(async () => { mongod = await MongoMemoryServer.create(); diff --git a/client/src/components/Chat/ChatView.tsx b/client/src/components/Chat/ChatView.tsx index f488d76b2..9c760e440 100644 --- a/client/src/components/Chat/ChatView.tsx +++ b/client/src/components/Chat/ChatView.tsx @@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil'; import { useForm } from 'react-hook-form'; import { Spinner } from '@librechat/client'; import { useParams } from 'react-router-dom'; -import { Constants } from 'librechat-data-provider'; +import { Constants, buildTree } from 'librechat-data-provider'; import type { TMessage } from 'librechat-data-provider'; import type { ChatFormValues } from '~/common'; import { ChatContext, AddedChatContext, useFileMapContext, ChatFormProvider } from '~/Providers'; @@ -12,11 +12,11 @@ import ConversationStarters from './Input/ConversationStarters'; import { useGetMessagesByConvoId } from '~/data-provider'; import MessagesView from './Messages/MessagesView'; import Presentation from './Presentation'; -import { buildTree, cn } from '~/utils'; import ChatForm from './Input/ChatForm'; import Landing from './Landing'; import Header from './Header'; import Footer from './Footer'; +import { cn } from '~/utils'; import store from '~/store'; function LoadingSpinner() { diff --git a/client/src/components/Share/ShareView.tsx b/client/src/components/Share/ShareView.tsx index 2d1f52ac7..b353c46c6 100644 --- a/client/src/components/Share/ShareView.tsx +++ b/client/src/components/Share/ShareView.tsx @@ -1,12 +1,12 @@ import { memo } from 'react'; import { Spinner } from '@librechat/client'; import { useParams } from 'react-router-dom'; +import { buildTree } from 'librechat-data-provider'; import { useGetSharedMessages } from 'librechat-data-provider/react-query'; import { useLocalize, useDocumentTitle } from '~/hooks'; import { useGetStartupConfig } from '~/data-provider'; import { ShareContext } from '~/Providers'; import MessagesView from './MessagesView'; -import { buildTree } from '~/utils'; import Footer from '../Chat/Footer'; function SharedView() { diff --git a/client/src/hooks/Conversations/useExportConversation.ts b/client/src/hooks/Conversations/useExportConversation.ts index e97f321e1..899189e20 100644 --- a/client/src/hooks/Conversations/useExportConversation.ts +++ b/client/src/hooks/Conversations/useExportConversation.ts @@ -4,6 +4,7 @@ import { useParams } from 'react-router-dom'; import exportFromJSON from 'export-from-json'; import { useQueryClient } from '@tanstack/react-query'; import { + buildTree, QueryKeys, ContentTypes, ToolCallTypes, @@ -18,7 +19,7 @@ import type { } from 'librechat-data-provider'; import useBuildMessageTree from '~/hooks/Messages/useBuildMessageTree'; import { useScreenshot } from '~/hooks/ScreenshotContext'; -import { cleanupPreset, buildTree } from '~/utils'; +import { cleanupPreset } from '~/utils'; type ExportValues = { fieldName: string; diff --git a/client/src/routes/Search.tsx b/client/src/routes/Search.tsx index f7992745c..4e6a180a1 100644 --- a/client/src/routes/Search.tsx +++ b/client/src/routes/Search.tsx @@ -1,12 +1,12 @@ import { useEffect, useMemo } from 'react'; import { useRecoilValue } from 'recoil'; +import { buildTree } from 'librechat-data-provider'; import { Spinner, useToastContext } from '@librechat/client'; import MinimalMessagesWrapper from '~/components/Chat/Messages/MinimalMessages'; import { useNavScrolling, useLocalize, useAuthContext } from '~/hooks'; import SearchMessage from '~/components/Chat/Messages/SearchMessage'; import { useMessagesInfiniteQuery } from '~/data-provider'; import { useFileMapContext } from '~/Providers'; -import { buildTree } from '~/utils'; import store from '~/store'; export default function Search() { diff --git a/client/src/utils/buildTree.ts b/client/src/utils/buildTree.ts index e6d0b28e2..836e87b50 100644 --- a/client/src/utils/buildTree.ts +++ b/client/src/utils/buildTree.ts @@ -1,49 +1,4 @@ -import { TFile, TMessage } from 'librechat-data-provider'; - -type ParentMessage = TMessage & { children: TMessage[]; depth: number }; -export default function buildTree({ - messages, - fileMap, -}: { - messages: TMessage[] | null; - fileMap?: Record; -}) { - if (messages === null) { - return null; - } - - const messageMap: Record = {}; - const rootMessages: TMessage[] = []; - const childrenCount: Record = {}; - - messages.forEach((message) => { - const parentId = message.parentMessageId ?? ''; - childrenCount[parentId] = (childrenCount[parentId] || 0) + 1; - - const extendedMessage: ParentMessage = { - ...message, - children: [], - depth: 0, - siblingIndex: childrenCount[parentId] - 1, - }; - - if (message.files && fileMap) { - extendedMessage.files = message.files.map((file) => fileMap[file.file_id ?? ''] ?? file); - } - - messageMap[message.messageId] = extendedMessage; - - const parentMessage = messageMap[parentId]; - if (parentMessage) { - parentMessage.children.push(extendedMessage); - extendedMessage.depth = parentMessage.depth + 1; - } else { - rootMessages.push(extendedMessage); - } - }); - - return rootMessages; -} +import type { TMessage } from 'librechat-data-provider'; const even = 'w-full border-b border-black/10 dark:border-gray-800/50 text-gray-800 bg-white dark:text-gray-200 group dark:bg-gray-800 hover:bg-gray-200/25 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200'; diff --git a/client/src/utils/index.ts b/client/src/utils/index.ts index 07a1caecb..ba3ba1aa3 100644 --- a/client/src/utils/index.ts +++ b/client/src/utils/index.ts @@ -21,7 +21,6 @@ export * from './promptGroups'; export * from './email'; export { default as cn } from './cn'; export { default as logger } from './logger'; -export { default as buildTree } from './buildTree'; export { default as scaleImage } from './scaleImage'; export { default as getLoginError } from './getLoginError'; export { default as cleanupPreset } from './cleanupPreset'; diff --git a/package-lock.json b/package-lock.json index a4b6510d2..0da060499 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52227,7 +52227,7 @@ }, "packages/data-provider": { "name": "librechat-data-provider", - "version": "0.8.004", + "version": "0.8.005", "license": "ISC", "dependencies": { "axios": "^1.8.2", diff --git a/packages/data-provider/package.json b/packages/data-provider/package.json index fca2e5cf6..0c2c1c27d 100644 --- a/packages/data-provider/package.json +++ b/packages/data-provider/package.json @@ -1,6 +1,6 @@ { "name": "librechat-data-provider", - "version": "0.8.004", + "version": "0.8.005", "description": "data services for librechat apps", "main": "dist/index.js", "module": "dist/index.es.js", diff --git a/packages/data-provider/src/api-endpoints.ts b/packages/data-provider/src/api-endpoints.ts index 59ede0905..8b6be0f5a 100644 --- a/packages/data-provider/src/api-endpoints.ts +++ b/packages/data-provider/src/api-endpoints.ts @@ -58,7 +58,7 @@ export const messages = (params: q.MessagesListParams) => { return `${messagesRoot}/${conversationId}`; } - return `${messagesRoot}{buildQuery(rest)}`; + return `${messagesRoot}${buildQuery(rest)}`; }; export const messagesArtifacts = (messageId: string) => `${messagesRoot}/artifacts/${messageId}`; diff --git a/packages/data-provider/src/index.ts b/packages/data-provider/src/index.ts index 90a55c3b7..3f51d34c4 100644 --- a/packages/data-provider/src/index.ts +++ b/packages/data-provider/src/index.ts @@ -3,6 +3,8 @@ export * from './azure'; export * from './bedrock'; export * from './config'; export * from './file-config'; +/* messages */ +export * from './messages'; /* artifacts */ export * from './artifacts'; /* schema helpers */ diff --git a/packages/data-provider/src/messages.ts b/packages/data-provider/src/messages.ts new file mode 100644 index 000000000..2550d1ad0 --- /dev/null +++ b/packages/data-provider/src/messages.ts @@ -0,0 +1,50 @@ +import type { TFile } from './types/files'; +import type { TMessage } from './types'; + +export type ParentMessage = TMessage & { children: TMessage[]; depth: number }; +export function buildTree({ + messages, + fileMap, +}: { + messages: (TMessage | undefined)[] | null; + fileMap?: Record; +}) { + if (messages === null) { + return null; + } + + const messageMap: Record = {}; + const rootMessages: TMessage[] = []; + const childrenCount: Record = {}; + + messages.forEach((message) => { + if (!message) { + return; + } + const parentId = message.parentMessageId ?? ''; + childrenCount[parentId] = (childrenCount[parentId] || 0) + 1; + + const extendedMessage: ParentMessage = { + ...message, + children: [], + depth: 0, + siblingIndex: childrenCount[parentId] - 1, + }; + + if (message.files && fileMap) { + extendedMessage.files = message.files.map((file) => fileMap[file.file_id ?? ''] ?? file); + } + + messageMap[message.messageId] = extendedMessage; + + const parentMessage = messageMap[parentId]; + if (parentMessage) { + parentMessage.children.push(extendedMessage); + extendedMessage.depth = parentMessage.depth + 1; + } else { + rootMessages.push(extendedMessage); + } + }); + + return rootMessages; +}