From ca2144a6c6403c74addf2c1cf5a6a30bc6d4467b Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Thu, 30 May 2024 15:15:24 +0300 Subject: [PATCH] feat: add a way to identify different message creators --- bun.lockb | Bin 232166 -> 232166 bytes index.ts | 1 + package.json | 12 +++++++----- src/constants.ts | 3 +++ src/history/in-memory-custom-history.test.ts | 11 +++++++++-- src/history/in-memory-custom-history.ts | 14 ++++++++++++-- src/history/index.ts | 14 ++++++++++++-- src/history/redis-custom-history.ts | 18 ++++++++++++++++-- src/rag-chat.ts | 7 ++++++- 9 files changed, 66 insertions(+), 14 deletions(-) diff --git a/bun.lockb b/bun.lockb index 7126c401bfaf2ff8e4c2d2618855b0fa80db6776..e6c5e5b260a072d3d648188c4e0ab9f49e3b63af 100755 GIT binary patch delta 31574 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 I#jjrcAL0@Bl>h($ diff --git a/index.ts b/index.ts index 52b2aa7..ac7e171 100644 --- a/index.ts +++ b/index.ts @@ -4,3 +4,4 @@ export * from "./src/database"; export * from "./src/ratelimit"; export * from "./src/error"; export * from "./src/types"; +export { MODEL_NAME_WITH_PROVIDER_SPLITTER } from "./src/constants"; diff --git a/package.json b/package.json index 4cd157e..3a7153c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@upstash/rag-chat", - "version": "0.0.25-alpha", + "version": "0.0.27-alpha", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -49,10 +49,6 @@ "dependencies": { "@langchain/community": "^0.2.1", "@langchain/core": "^0.1.58", - "@langchain/openai": "^0.0.28", - "@upstash/ratelimit": "^1.1.3", - "@upstash/redis": "^1.31.1", - "@upstash/vector": "^1.1.1", "ai": "^3.1.1", "cheerio": "^1.0.0-rc.12", "d3-dsv": "^3.0.1", @@ -60,5 +56,11 @@ "langchain": "^0.2.0", "nanoid": "^5.0.7", "pdf-parse": "^1.1.1" + }, + "peerDependencies": { + "@upstash/redis": "^1.31.3", + "@upstash/vector": "^1.1.1", + "@upstash/ratelimit": "^1.1.3", + "@langchain/openai": "^0.0.34" } } diff --git a/src/constants.ts b/src/constants.ts index 1ae7f91..17ac760 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -14,3 +14,6 @@ export const DEFAULT_METADATA_KEY = "text"; //History related default options export const DEFAULT_HISTORY_TTL = 86_400; export const DEFAULT_HISTORY_LENGTH = 5; + +//We need that constant to split creator LLM such as `ChatOpenAI_gpt-3.5-turbo`. Format is `provider_modelName`. +export const MODEL_NAME_WITH_PROVIDER_SPLITTER = "_"; diff --git a/src/history/in-memory-custom-history.test.ts b/src/history/in-memory-custom-history.test.ts index f77b26f..9b4272f 100644 --- a/src/history/in-memory-custom-history.test.ts +++ b/src/history/in-memory-custom-history.test.ts @@ -3,7 +3,11 @@ import { CustomInMemoryChatMessageHistory } from "./in-memory-custom-history"; test("should give last 3 messages from in-memory", async () => { const messageHistoryLength = 3; - const history = new CustomInMemoryChatMessageHistory([], messageHistoryLength); + const history = new CustomInMemoryChatMessageHistory({ + messages: [], + topLevelChatHistoryLength: messageHistoryLength, + modelNameWithProvider: "", + }); await history.addUserMessage("Hello!"); await history.addAIMessage("Hello, human."); await history.addUserMessage("Whats your name?"); @@ -16,7 +20,10 @@ test("should give last 3 messages from in-memory", async () => { }); test("should give all the messages", async () => { - const history = new CustomInMemoryChatMessageHistory(); + const history = new CustomInMemoryChatMessageHistory({ + messages: [], + modelNameWithProvider: "", + }); await history.addUserMessage("Hello!"); await history.addAIMessage("Hello, human."); await history.addUserMessage("Whats your name?"); diff --git a/src/history/in-memory-custom-history.ts b/src/history/in-memory-custom-history.ts index d6e235c..e75ff51 100644 --- a/src/history/in-memory-custom-history.ts +++ b/src/history/in-memory-custom-history.ts @@ -2,17 +2,26 @@ import { BaseListChatMessageHistory } from "@langchain/core/chat_history"; import type { BaseMessage } from "@langchain/core/messages"; +export type CustomInMemoryChatMessageHistoryInput = { + messages?: BaseMessage[]; + topLevelChatHistoryLength?: number; + modelNameWithProvider: string; +}; + export class CustomInMemoryChatMessageHistory extends BaseListChatMessageHistory { lc_namespace = ["langchain", "stores", "message", "in_memory"]; private messages: BaseMessage[] = []; private topLevelChatHistoryLength?: number; + private modelNameWithProvider: string; - constructor(messages?: BaseMessage[], topLevelChatHistoryLength?: number) { + constructor(fields: CustomInMemoryChatMessageHistoryInput) { + const { modelNameWithProvider, messages, topLevelChatHistoryLength } = fields; // eslint-disable-next-line prefer-rest-params super(...arguments); this.messages = messages ?? []; this.topLevelChatHistoryLength = topLevelChatHistoryLength; + this.modelNameWithProvider = modelNameWithProvider; } /** @@ -32,7 +41,8 @@ export class CustomInMemoryChatMessageHistory extends BaseListChatMessageHistory * @returns A promise that resolves when the message has been added. */ async addMessage(message: BaseMessage) { - this.messages.push(message); + //@ts-expect-error This our way of mutating Message object to store model name with providers. + this.messages.push({ ...message, modelNameWithProvider: this.modelNameWithProvider }); } /** diff --git a/src/history/index.ts b/src/history/index.ts index b9c49c7..e0e1017 100644 --- a/src/history/index.ts +++ b/src/history/index.ts @@ -3,16 +3,25 @@ import { CustomInMemoryChatMessageHistory } from "./in-memory-custom-history"; import { CustomUpstashRedisChatMessageHistory } from "./redis-custom-history"; import { InternalUpstashError } from "../error"; +type HistoryConfig = { + redis?: Redis; + modelNameWithProvider: string; +}; type GetHistory = { sessionId: string; length?: number; sessionTTL?: number }; export class History { private redis?: Redis; + private modelNameWithProvider: string; private inMemoryChatHistory?: CustomInMemoryChatMessageHistory; - constructor(redis?: Redis) { + constructor(fields: HistoryConfig) { + const { modelNameWithProvider, redis } = fields; + this.redis = redis; + this.modelNameWithProvider = modelNameWithProvider; + if (!redis) { - this.inMemoryChatHistory = new CustomInMemoryChatMessageHistory(); + this.inMemoryChatHistory = new CustomInMemoryChatMessageHistory({ modelNameWithProvider }); } } @@ -24,6 +33,7 @@ export class History { sessionTTL, topLevelChatHistoryLength: length, client: this.redis, + modelNameWithProvider: this.modelNameWithProvider, }); } } catch (error) { diff --git a/src/history/redis-custom-history.ts b/src/history/redis-custom-history.ts index 6369704..b4f6ff7 100644 --- a/src/history/redis-custom-history.ts +++ b/src/history/redis-custom-history.ts @@ -19,6 +19,7 @@ export type CustomUpstashRedisChatMessageHistoryInput = { config?: RedisConfigNodejs; client?: Redis; topLevelChatHistoryLength?: number; + modelNameWithProvider: string; }; /** @@ -38,13 +39,21 @@ export class CustomUpstashRedisChatMessageHistory extends BaseListChatMessageHis public client: Redis; private sessionId: string; + private modelNameWithProvider: string; private sessionTTL?: number; private topLevelChatHistoryLength?: number; constructor(fields: CustomUpstashRedisChatMessageHistoryInput) { super(fields); - const { sessionId, sessionTTL, config, client, topLevelChatHistoryLength } = fields; + const { + sessionId, + sessionTTL, + config, + client, + topLevelChatHistoryLength, + modelNameWithProvider, + } = fields; if (client) { this.client = client; } else if (config) { @@ -54,7 +63,9 @@ export class CustomUpstashRedisChatMessageHistory extends BaseListChatMessageHis `Upstash Redis message stores require either a config object or a pre-configured client.` ); } + this.sessionId = sessionId; + this.modelNameWithProvider = modelNameWithProvider; this.sessionTTL = sessionTTL; this.topLevelChatHistoryLength = topLevelChatHistoryLength; } @@ -86,7 +97,10 @@ export class CustomUpstashRedisChatMessageHistory extends BaseListChatMessageHis */ async addMessage(message: BaseMessage): Promise { const messageToAdd = mapChatMessagesToStoredMessages([message]); - await this.client.lpush(this.sessionId, JSON.stringify(messageToAdd[0])); + await this.client.lpush( + this.sessionId, + JSON.stringify({ ...messageToAdd[0], modelNameWithProvider: this.modelNameWithProvider }) + ); if (this.sessionTTL) { await this.client.expire(this.sessionId, this.sessionTTL); } diff --git a/src/rag-chat.ts b/src/rag-chat.ts index 1450aa4..7a97c63 100644 --- a/src/rag-chat.ts +++ b/src/rag-chat.ts @@ -14,6 +14,7 @@ import { appendDefaultsIfNeeded } from "./utils"; import type { AddContextOptions, AddContextPayload } from "./database"; import { Database } from "./database"; import { History } from "./history"; +import { MODEL_NAME_WITH_PROVIDER_SPLITTER } from "./constants.ts"; export class RAGChat extends RAGChatBase { #ratelimitService: RateLimitService; @@ -21,7 +22,11 @@ export class RAGChat extends RAGChatBase { constructor(config: RAGChatConfig) { const { vector: index, redis } = new Config(config); - const historyService = new History(redis); + const historyService = new History({ + redis, + //@ts-expect-error We need that private field to track message creator LLM such as `ChatOpenAI_gpt-3.5-turbo`. Format is `provider_modelName`. + modelNameWithProvider: `${config.model?.getName()}${MODEL_NAME_WITH_PROVIDER_SPLITTER}${config.model?.modelName}`, + }); const vectorService = new Database(index); const ratelimitService = new RateLimitService(config.ratelimit);