diff --git a/packages/chatbot-server-mongodb-public/src/config.ts b/packages/chatbot-server-mongodb-public/src/config.ts index 3ee981c03..3d2d02cf2 100644 --- a/packages/chatbot-server-mongodb-public/src/config.ts +++ b/packages/chatbot-server-mongodb-public/src/config.ts @@ -16,9 +16,10 @@ import { requireRequestOrigin, AddCustomDataFunc, makeDefaultFindVerifiedAnswer, - defaultCreateConversationCustomData, - defaultAddMessageToConversationCustomData, makeVerifiedAnswerGenerateResponse, + addDefaultCustomData, + ConversationsRouterLocals, + SearchContentRouterLocals, } from "mongodb-chatbot-server"; import cookieParser from "cookie-parser"; import { blockGetRequests } from "./middleware/blockGetRequests"; @@ -269,7 +270,7 @@ export const generateResponse = wrapTraced( export const createConversationCustomDataWithAuthUser: AddCustomDataFunc = async (req, res) => { - const customData = await defaultCreateConversationCustomData(req, res); + const customData = await addDefaultCustomData(req, res); if (req.cookies.auth_user) { customData.authUser = req.cookies.auth_user; } @@ -318,12 +319,13 @@ export const config: AppConfig = { classifierModel: languageModel, }), searchResultsStore, + middleware: [requireValidIpAddress(), requireRequestOrigin()], }, conversationsRouterConfig: { middleware: [ blockGetRequests, - requireValidIpAddress(), - requireRequestOrigin(), + requireValidIpAddress(), + requireRequestOrigin(), useSegmentIds(), redactConnectionUri(), cookieParser(), @@ -332,10 +334,7 @@ export const config: AppConfig = { ? createConversationCustomDataWithAuthUser : undefined, addMessageToConversationCustomData: async (req, res) => { - const defaultCustomData = await defaultAddMessageToConversationCustomData( - req, - res - ); + const defaultCustomData = await addDefaultCustomData(req, res); const customData = { ...defaultCustomData, }; diff --git a/packages/chatbot-server-mongodb-public/src/processors/findContentWithMongoDbMetadata.ts b/packages/chatbot-server-mongodb-public/src/processors/findContentWithMongoDbMetadata.ts index a08d9f21f..854038b57 100644 --- a/packages/chatbot-server-mongodb-public/src/processors/findContentWithMongoDbMetadata.ts +++ b/packages/chatbot-server-mongodb-public/src/processors/findContentWithMongoDbMetadata.ts @@ -31,7 +31,7 @@ export const makeFindContentWithMongoDbMetadata = ({ return res; }, { - name: "makeFindContentWithMongoDbMetadata", + name: "findContentWithMongoDbMetadata", } ); return wrappedFindContent; diff --git a/packages/mongodb-chatbot-server/src/middleware/requireRequestOrigin.ts b/packages/mongodb-chatbot-server/src/middleware/requireRequestOrigin.ts index 1f6567e04..61e6b68b7 100644 --- a/packages/mongodb-chatbot-server/src/middleware/requireRequestOrigin.ts +++ b/packages/mongodb-chatbot-server/src/middleware/requireRequestOrigin.ts @@ -1,9 +1,13 @@ +import { RequestHandler } from "express"; +import { ParamsDictionary } from "express-serve-static-core"; +import { ParsedQs } from "qs"; import { getRequestId, logRequest, sendErrorResponse } from "../utils"; -import { ConversationsMiddleware } from "../routes/conversations/conversationsRouter"; export const CUSTOM_REQUEST_ORIGIN_HEADER = "X-Request-Origin"; -export function requireRequestOrigin(): ConversationsMiddleware { +export function requireRequestOrigin< + Locals extends Record +>(): RequestHandler { return (req, res, next) => { const reqId = getRequestId(req); diff --git a/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.test.ts b/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.test.ts index 6f946c24a..8bb027e1e 100644 --- a/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.test.ts +++ b/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.test.ts @@ -3,6 +3,7 @@ import { createConversationsMiddlewareReq, createConversationsMiddlewareRes, } from "../test/middlewareTestHelpers"; +import { ConversationsRouterLocals } from "../routes"; const baseReq = { body: { message: "Hello, world!" }, @@ -18,7 +19,7 @@ describe("requireValidIpAddress", () => { const res = createConversationsMiddlewareRes(); const next = jest.fn(); - const middleware = requireValidIpAddress(); + const middleware = requireValidIpAddress(); req.body = baseReq.body; req.params = baseReq.params; req.query = baseReq.query; @@ -39,7 +40,7 @@ describe("requireValidIpAddress", () => { const next = jest.fn(); const invalidIpAddress = "not-an-ip-address"; - const middleware = requireValidIpAddress(); + const middleware = requireValidIpAddress(); req.body = baseReq.body; req.params = baseReq.params; req.query = baseReq.query; @@ -59,7 +60,7 @@ describe("requireValidIpAddress", () => { const res = createConversationsMiddlewareRes(); const next = jest.fn(); - const middleware = requireValidIpAddress(); + const middleware = requireValidIpAddress(); req.body = baseReq.body; req.params = baseReq.params; req.query = baseReq.query; diff --git a/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.ts b/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.ts index a4e3914dc..41dd77f76 100644 --- a/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.ts +++ b/packages/mongodb-chatbot-server/src/middleware/requireValidIpAddress.ts @@ -1,8 +1,12 @@ +import { RequestHandler } from "express"; +import { ParamsDictionary } from "express-serve-static-core"; +import { ParsedQs } from "qs"; import { getRequestId, logRequest, sendErrorResponse } from "../utils"; -import { ConversationsMiddleware } from "../routes/conversations/conversationsRouter"; import { isValidIp } from "../routes/conversations/utils"; -export function requireValidIpAddress(): ConversationsMiddleware { +export function requireValidIpAddress< + Locals extends Record +>(): RequestHandler { return (req, res, next) => { const reqId = getRequestId(req); diff --git a/packages/mongodb-chatbot-server/src/middleware/validateRequestSchema.ts b/packages/mongodb-chatbot-server/src/middleware/validateRequestSchema.ts index 0fd952675..608cf6612 100644 --- a/packages/mongodb-chatbot-server/src/middleware/validateRequestSchema.ts +++ b/packages/mongodb-chatbot-server/src/middleware/validateRequestSchema.ts @@ -5,12 +5,12 @@ import { getRequestId, logRequest, sendErrorResponse } from "../utils"; export const SomeExpressRequest = z.object({ headers: z.object({}).optional(), - params: z.object({}).optional(), + params: z.object({}), query: z.object({}).optional(), body: z.object({}).optional(), }); -function generateZodErrorMessage(error: ZodError) { +export function generateZodErrorMessage(error: ZodError) { return generateErrorMessage(error.issues, { delimiter: { error: "\n", diff --git a/packages/mongodb-chatbot-server/src/processors/addCustomData.ts b/packages/mongodb-chatbot-server/src/processors/addCustomData.ts new file mode 100644 index 000000000..6b66f194b --- /dev/null +++ b/packages/mongodb-chatbot-server/src/processors/addCustomData.ts @@ -0,0 +1,94 @@ +import { Request, Response } from "express"; + +export type RequestCustomData = Record | undefined; + +/** + Function to add custom data to the {@link Conversation} or content search Request persisted to the database. + Has access to the Express.js request and response plus the values + from the {@link Response.locals} object. + */ +export type AddCustomDataFunc = ( + request: Request, + response: Response +) => Promise; + +const addIpToCustomData: AddCustomDataFunc = async (req) => + req.ip + ? { + ip: req.ip, + } + : undefined; + +const addOriginToCustomData: AddCustomDataFunc = async (_, res) => + res.locals.customData.origin + ? { + origin: res.locals.customData.origin, + } + : undefined; + +export const originCodes = [ + "LEARN", + "DEVELOPER", + "DOCS", + "DOTCOM", + "GEMINI_CODE_ASSIST", + "VSCODE", + "OTHER", +] as const; + +export type OriginCode = (typeof originCodes)[number]; + +interface OriginRule { + regex: RegExp; + code: OriginCode; +} + +const ORIGIN_RULES: OriginRule[] = [ + { regex: /learn\.mongodb\.com/, code: "LEARN" }, + { regex: /mongodb\.com\/developer/, code: "DEVELOPER" }, + { regex: /mongodb\.com\/docs/, code: "DOCS" }, + { regex: /mongodb\.com\//, code: "DOTCOM" }, + { regex: /google-gemini-code-assist/, code: "GEMINI_CODE_ASSIST" }, + { regex: /vscode-mongodb-copilot/, code: "VSCODE" }, +]; + +function getOriginCode(origin: string): OriginCode { + for (const rule of ORIGIN_RULES) { + if (rule.regex.test(origin)) { + return rule.code; + } + } + return "OTHER"; +} + +const addOriginCodeToCustomData: AddCustomDataFunc = async (_, res) => { + const origin = res.locals.customData.origin; + return typeof origin === "string" && origin.length > 0 + ? { + originCode: getOriginCode(origin), + } + : undefined; +}; + +const addUserAgentToCustomData: AddCustomDataFunc = async (req) => + req.headers["user-agent"] + ? { + userAgent: req.headers["user-agent"], + } + : undefined; + +export type AddDefinedCustomDataFunc = ( + ...args: Parameters +) => Promise>; + +export const addDefaultCustomData: AddDefinedCustomDataFunc = async ( + req, + res +) => { + return { + ...(await addIpToCustomData(req, res)), + ...(await addOriginToCustomData(req, res)), + ...(await addOriginCodeToCustomData(req, res)), + ...(await addUserAgentToCustomData(req, res)), + }; +}; diff --git a/packages/mongodb-chatbot-server/src/processors/index.ts b/packages/mongodb-chatbot-server/src/processors/index.ts index 1f7975e67..399d44aed 100644 --- a/packages/mongodb-chatbot-server/src/processors/index.ts +++ b/packages/mongodb-chatbot-server/src/processors/index.ts @@ -9,3 +9,4 @@ export * from "./InputGuardrail"; export * from "./makeVerifiedAnswerGenerateResponse"; export * from "./includeChunksForMaxTokensPossible"; export * from "./GenerateResponse"; +export * from "./addCustomData"; diff --git a/packages/mongodb-chatbot-server/src/routes/content/contentRouter.test.ts b/packages/mongodb-chatbot-server/src/routes/content/contentRouter.test.ts index 306b6bdca..0dd7f8a27 100644 --- a/packages/mongodb-chatbot-server/src/routes/content/contentRouter.test.ts +++ b/packages/mongodb-chatbot-server/src/routes/content/contentRouter.test.ts @@ -1,10 +1,14 @@ +import { Express } from "express"; import request from "supertest"; -import { makeTestApp } from "../../test/testHelpers"; -import type { MakeContentRouterParams } from "./contentRouter"; import type { FindContentFunc, MongoDbSearchResultsStore, } from "mongodb-rag-core"; +import type { + MakeContentRouterParams, + SearchContentMiddleware, +} from "./contentRouter"; +import { makeTestApp } from "../../test/testHelpers"; // Minimal in-memory mock for SearchResultsStore for testing purposes const mockSearchResultsStore: MongoDbSearchResultsStore = { @@ -23,8 +27,7 @@ const findContentMock = jest.fn().mockResolvedValue({ queryEmbedding: [], }) satisfies FindContentFunc; -// Helper to build contentRouterConfig for the test app -function makeContentRouterConfig( +function makeMockContentRouterConfig( overrides: Partial = {} ) { return { @@ -35,20 +38,57 @@ function makeContentRouterConfig( } describe("contentRouter", () => { + const ipAddress = "127.0.0.1"; const searchEndpoint = "/api/v1/content/search"; it("should call custom middleware if provided", async () => { const mockMiddleware = jest.fn((_req, _res, next) => next()); const { app, origin } = await makeTestApp({ - contentRouterConfig: makeContentRouterConfig({ + contentRouterConfig: makeMockContentRouterConfig({ middleware: [mockMiddleware], }), }); - await request(app) - .post(searchEndpoint) - .set("req-id", "test-req-id") - .set("Origin", origin) - .send({ query: "mongodb" }); + await createContentReq({ app, origin, query: "mongodb" }); expect(mockMiddleware).toHaveBeenCalled(); }); + + test("should use route middleware customData", async () => { + const middleware1: SearchContentMiddleware = (_, res, next) => { + res.locals.customData.middleware1 = true; + next(); + }; + let called = false; + const middleware2: SearchContentMiddleware = (_, res, next) => { + expect(res.locals.customData.middleware1).toBe(true); + called = true; + next(); + }; + const { app, origin } = await makeTestApp({ + contentRouterConfig: makeMockContentRouterConfig({ + middleware: [middleware1, middleware2], + }), + }); + await createContentReq({ app, origin, query: "What is aggregation?" }); + expect(called).toBe(true); + }); + + /** + Helper function to create a new content request + */ + async function createContentReq({ + app, + origin, + query, + }: { + app: Express; + origin: string; + query: string; + }) { + const createContentRes = await request(app) + .post(searchEndpoint) + .set("X-FORWARDED-FOR", ipAddress) + .set("Origin", origin) + .send({ query }); + return createContentRes; + } }); diff --git a/packages/mongodb-chatbot-server/src/routes/content/contentRouter.ts b/packages/mongodb-chatbot-server/src/routes/content/contentRouter.ts index 31fd333ac..fe40a3241 100644 --- a/packages/mongodb-chatbot-server/src/routes/content/contentRouter.ts +++ b/packages/mongodb-chatbot-server/src/routes/content/contentRouter.ts @@ -1,9 +1,18 @@ -import { RequestHandler, Router } from "express"; +import { NextFunction, RequestHandler, Response, Router } from "express"; import { ParamsDictionary } from "express-serve-static-core"; import { FindContentFunc, MongoDbSearchResultsStore } from "mongodb-rag-core"; +import { ParsedQs } from "qs"; import validateRequestSchema from "../../middleware/validateRequestSchema"; import { SearchContentRequest, makeSearchContentRoute } from "./searchContent"; +import { requireRequestOrigin, requireValidIpAddress } from "../../middleware"; +import { + AddCustomDataFunc, + addDefaultCustomData, + RequestCustomData, +} from "../../processors"; + +export type SearchContentCustomData = RequestCustomData; /** Middleware to put in front of all the routes in the contentRouter. @@ -17,7 +26,7 @@ export type SearchContentMiddleware = RequestHandler< ParamsDictionary, unknown, unknown, - unknown, + ParsedQs, SearchContentRouterLocals >; @@ -30,20 +39,40 @@ export interface SearchContentRouterLocals { customData: Record; } +/** + Express.js Response from the app's {@link ConversationsService}. + */ +export type SearchContentRouterResponse = Response< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any, + SearchContentRouterLocals +>; + export interface MakeContentRouterParams { findContent: FindContentFunc; searchResultsStore: MongoDbSearchResultsStore; - // TODO: Add default middleware along with customData as in conversationsRouter + addCustomData?: AddCustomDataFunc; middleware?: SearchContentMiddleware[]; } export function makeContentRouter({ findContent, searchResultsStore, - middleware = [], + addCustomData = addDefaultCustomData, + middleware = [ + requireValidIpAddress(), + requireRequestOrigin(), + ], }: MakeContentRouterParams) { const contentRouter = Router(); + // Set the customData and conversations on the response locals + // for use in subsequent middleware. + contentRouter.use(((_, res: Response, next: NextFunction) => { + res.locals.customData = {}; + next(); + }) satisfies RequestHandler); + // Add middleware to the conversationsRouter. middleware?.forEach((middleware) => contentRouter.use(middleware)); @@ -54,6 +83,7 @@ export function makeContentRouter({ makeSearchContentRoute({ findContent, searchResultsStore, + addCustomData, }) ); diff --git a/packages/mongodb-chatbot-server/src/routes/content/searchContent.ts b/packages/mongodb-chatbot-server/src/routes/content/searchContent.ts index 7de4a2a89..bdfbbc6df 100644 --- a/packages/mongodb-chatbot-server/src/routes/content/searchContent.ts +++ b/packages/mongodb-chatbot-server/src/routes/content/searchContent.ts @@ -12,9 +12,14 @@ import { } from "mongodb-rag-core"; import { z } from "zod"; -import { SomeExpressRequest } from "../../middleware"; +import { generateZodErrorMessage, SomeExpressRequest } from "../../middleware"; import { makeRequestError } from "../conversations/utils"; -import { SearchContentRouterLocals } from "./contentRouter"; +import { + SearchContentCustomData, + SearchContentRouterLocals, +} from "./contentRouter"; +import { AddCustomDataFunc } from "../../processors"; +import { wrapTraced } from "mongodb-rag-core/braintrust"; export const SearchContentRequestBody = z.object({ query: z.string(), @@ -37,6 +42,7 @@ export type SearchContentRequestBody = z.infer; export interface MakeSearchContentRouteParams { findContent: FindContentFunc; searchResultsStore: MongoDbSearchResultsStore; + addCustomData?: AddCustomDataFunc; } interface SearchContentResponseChunk { @@ -58,25 +64,39 @@ interface SearchContentResponseBody { export function makeSearchContentRoute({ findContent, searchResultsStore, + addCustomData, }: MakeSearchContentRouteParams) { + const tracedFindContent = wrapTraced(findContent, { name: "searchContent" }); return async ( req: ExpressRequest, res: ExpressResponse ) => { try { + // --- INPUT VALIDATION --- + const { error } = SearchContentRequestBody.safeParse(req.body); + if (error) { + throw makeRequestError({ + httpStatus: 500, + message: generateZodErrorMessage(error), + }); + } + const { query, dataSources, limit } = req.body; - const results = await findContent({ + const results = await tracedFindContent({ query, filters: mapDataSourcesToFilters(dataSources), limit, }); res.json(mapFindContentResultToSearchContentResponseChunk(results)); + + const customData = await getCustomData(req, res, addCustomData); await persistSearchResultsToDatabase({ query, results, dataSources, limit, searchResultsStore, + customData, }); } catch (error) { throw makeRequestError({ @@ -122,18 +142,44 @@ function mapDataSourcesToFilters( }; } -async function persistSearchResultsToDatabase(params: { +async function persistSearchResultsToDatabase({ + query, + results, + dataSources, + limit, + searchResultsStore, + customData, +}: { query: string; results: FindContentResult; dataSources: SearchRecordDataSource[]; limit: number; searchResultsStore: MongoDbSearchResultsStore; + customData?: { [k: string]: unknown }; }) { - params.searchResultsStore.saveSearchResult({ - query: params.query, - results: params.results.content, - dataSources: params.dataSources, - limit: params.limit, + searchResultsStore.saveSearchResult({ + query, + results: results.content, + dataSources, + limit, createdAt: new Date(), + ...(customData !== undefined && { customData }), }); } + +async function getCustomData( + req: ExpressRequest, + res: ExpressResponse, + addCustomData?: AddCustomDataFunc +): Promise { + try { + if (addCustomData) { + return await addCustomData(req, res); + } + } catch (error) { + throw makeRequestError({ + httpStatus: 500, + message: "Error parsing custom data from the request", + }); + } +} diff --git a/packages/mongodb-chatbot-server/src/routes/conversations/addMessageToConversation.ts b/packages/mongodb-chatbot-server/src/routes/conversations/addMessageToConversation.ts index 24b150ffe..bf4b0b8f2 100644 --- a/packages/mongodb-chatbot-server/src/routes/conversations/addMessageToConversation.ts +++ b/packages/mongodb-chatbot-server/src/routes/conversations/addMessageToConversation.ts @@ -22,7 +22,6 @@ import { getRequestId, logRequest, sendErrorResponse } from "../../utils"; import { z } from "zod"; import { SomeExpressRequest } from "../../middleware/validateRequestSchema"; import { - AddCustomDataFunc, ConversationsRouterLocals, } from "./conversationsRouter"; import { wrapTraced, Logger } from "mongodb-rag-core/braintrust"; @@ -31,6 +30,7 @@ import { GenerateResponse, GenerateResponseParams, } from "../../processors/GenerateResponse"; +import { AddCustomDataFunc } from "../../processors"; export const DEFAULT_MAX_INPUT_LENGTH = 3000; // magic number for max input size for LLM export const DEFAULT_MAX_USER_MESSAGES_IN_CONVERSATION = 7; // magic number for max messages in a conversation diff --git a/packages/mongodb-chatbot-server/src/routes/conversations/conversationsRouter.ts b/packages/mongodb-chatbot-server/src/routes/conversations/conversationsRouter.ts index d98f1e6ce..892432a79 100644 --- a/packages/mongodb-chatbot-server/src/routes/conversations/conversationsRouter.ts +++ b/packages/mongodb-chatbot-server/src/routes/conversations/conversationsRouter.ts @@ -32,6 +32,7 @@ import { import { UpdateTraceFunc } from "./UpdateTraceFunc"; import { GenerateResponse } from "../../processors/GenerateResponse"; import { Logger } from "mongodb-rag-core/braintrust"; +import { AddCustomDataFunc, addDefaultCustomData } from "../../processors"; /** Configuration for rate limiting on the /conversations/* routes. @@ -62,16 +63,6 @@ export interface ConversationsRateLimitConfig { addMessageSlowDownConfig?: SlowDownOptions; } -/** - Function to add custom data to the {@link Conversation} persisted to the database. - Has access to the Express.js request and response plus the {@link ConversationsRouterLocals} - from the {@link Response.locals} object. - */ -export type AddCustomDataFunc = ( - request: Request, - response: ConversationsRouterResponse -) => Promise; - /** Express.js Request that exposes the app's {@link ConversationsService}. @@ -193,95 +184,6 @@ export interface ConversationsRouterParams { braintrustLogger?: Logger; } -const addIpToCustomData: AddCustomDataFunc = async (req) => - req.ip - ? { - ip: req.ip, - } - : undefined; - -const addOriginToCustomData: AddCustomDataFunc = async (_, res) => - res.locals.customData.origin - ? { - origin: res.locals.customData.origin, - } - : undefined; - -export const originCodes = [ - "LEARN", - "DEVELOPER", - "DOCS", - "DOTCOM", - "GEMINI_CODE_ASSIST", - "VSCODE", - "OTHER", -] as const; - -export type OriginCode = (typeof originCodes)[number]; - -interface OriginRule { - regex: RegExp; - code: OriginCode; -} - -const ORIGIN_RULES: OriginRule[] = [ - { regex: /learn\.mongodb\.com/, code: "LEARN" }, - { regex: /mongodb\.com\/developer/, code: "DEVELOPER" }, - { regex: /mongodb\.com\/docs/, code: "DOCS" }, - { regex: /mongodb\.com\//, code: "DOTCOM" }, - { regex: /google-gemini-code-assist/, code: "GEMINI_CODE_ASSIST" }, - { regex: /vscode-mongodb-copilot/, code: "VSCODE" }, -]; - -function getOriginCode(origin: string): OriginCode { - for (const rule of ORIGIN_RULES) { - if (rule.regex.test(origin)) { - return rule.code; - } - } - return "OTHER"; -} - -const addOriginCodeToCustomData: AddCustomDataFunc = async (_, res) => { - const origin = res.locals.customData.origin; - return typeof origin === "string" && origin.length > 0 - ? { - originCode: getOriginCode(origin), - } - : undefined; -}; - -const addUserAgentToCustomData: AddCustomDataFunc = async (req) => - req.headers["user-agent"] - ? { - userAgent: req.headers["user-agent"], - } - : undefined; - -export type AddDefinedCustomDataFunc = ( - ...args: Parameters -) => Promise>; - -export const defaultCreateConversationCustomData: AddDefinedCustomDataFunc = - async (req, res) => { - return { - ...(await addIpToCustomData(req, res)), - ...(await addOriginToCustomData(req, res)), - ...(await addOriginCodeToCustomData(req, res)), - ...(await addUserAgentToCustomData(req, res)), - }; - }; - -export const defaultAddMessageToConversationCustomData: AddDefinedCustomDataFunc = - async (req, res) => { - return { - ...(await addIpToCustomData(req, res)), - ...(await addOriginToCustomData(req, res)), - ...(await addOriginCodeToCustomData(req, res)), - ...(await addUserAgentToCustomData(req, res)), - }; - }; - /** Constructor function to make the /conversations/* Express.js router. */ @@ -291,9 +193,9 @@ export function makeConversationsRouter({ maxInputLengthCharacters, maxUserMessagesInConversation, rateLimitConfig, - middleware = [requireValidIpAddress(), requireRequestOrigin()], - createConversationCustomData = defaultCreateConversationCustomData, - addMessageToConversationCustomData = defaultAddMessageToConversationCustomData, + middleware = [requireValidIpAddress(), requireRequestOrigin()], + createConversationCustomData = addDefaultCustomData, + addMessageToConversationCustomData = addDefaultCustomData, addMessageToConversationUpdateTrace, rateMessageUpdateTrace, commentMessageUpdateTrace, diff --git a/packages/mongodb-chatbot-server/src/routes/conversations/createConversation.ts b/packages/mongodb-chatbot-server/src/routes/conversations/createConversation.ts index ef6aec2f5..4481af5d9 100644 --- a/packages/mongodb-chatbot-server/src/routes/conversations/createConversation.ts +++ b/packages/mongodb-chatbot-server/src/routes/conversations/createConversation.ts @@ -17,9 +17,9 @@ import { import { getRequestId, logRequest, sendErrorResponse } from "../../utils"; import { SomeExpressRequest } from "../../middleware/validateRequestSchema"; import { - AddCustomDataFunc, ConversationsRouterLocals, } from "./conversationsRouter"; +import { AddCustomDataFunc } from "../../processors"; export type CreateConversationRequest = z.infer< typeof CreateConversationRequest diff --git a/packages/mongodb-chatbot-server/src/test/middlewareTestHelpers.ts b/packages/mongodb-chatbot-server/src/test/middlewareTestHelpers.ts index 44cedc8fb..7eb9d7b0e 100644 --- a/packages/mongodb-chatbot-server/src/test/middlewareTestHelpers.ts +++ b/packages/mongodb-chatbot-server/src/test/middlewareTestHelpers.ts @@ -2,6 +2,7 @@ import { Request } from "express"; import { ParamsDictionary } from "express-serve-static-core"; import { createRequest, createResponse } from "node-mocks-http"; import { ConversationsService } from "mongodb-rag-core"; +import { ParsedQs } from "qs"; import { ConversationsRouterLocals, ConversationsRouterResponse, @@ -13,7 +14,7 @@ export const createConversationsMiddlewareReq = () => ParamsDictionary, unknown, unknown, - unknown, + ParsedQs, ConversationsRouterLocals > >(); diff --git a/packages/mongodb-rag-core/src/contentStore/MongoDbSearchResultsStore.ts b/packages/mongodb-rag-core/src/contentStore/MongoDbSearchResultsStore.ts index f9fa77c79..df588ab70 100644 --- a/packages/mongodb-rag-core/src/contentStore/MongoDbSearchResultsStore.ts +++ b/packages/mongodb-rag-core/src/contentStore/MongoDbSearchResultsStore.ts @@ -47,6 +47,7 @@ export const SearchResultRecordSchema = z.object({ dataSources: z.array(SearchRecordDataSourceSchema).optional(), limit: z.number().optional(), createdAt: z.date(), + customData: z.object({}).passthrough().optional(), }); export interface SearchResultRecord { @@ -55,6 +56,7 @@ export interface SearchResultRecord { dataSources?: SearchRecordDataSource[]; limit?: number; createdAt: Date; + customData?: Record; } export type MongoDbSearchResultsStore = DatabaseConnection & {