Skip to content

Commit

Permalink
Merge pull request #12 from upstash/DX-1003
Browse files Browse the repository at this point in the history
feat: allow offsetting chat history
  • Loading branch information
ogzhanolguncu authored Jun 13, 2024
2 parents 142f46f + 9045d9a commit adea3ab
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@upstash/rag-chat",
"version": "0.0.34-alpha",
"version": "0.0.35-alpha",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
Expand Down
25 changes: 25 additions & 0 deletions src/history/in-memory-custom-history.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,28 @@ test("should give all the messages", async () => {
const final = (await history.getMessages()).map((message) => message.content as string);
expect(["Hello!", "Hello, human.", "Whats your name?", "Upstash", "Good."]).toEqual(final);
});

test("should give all the messages with offset pagination", async () => {
const history = new CustomInMemoryChatMessageHistory({
messages: [],
metadata: { helloWorld: "hey" },
});
await history.addUserMessage("Hello!");
await history.addAIMessage("Hello, human.");
await history.addUserMessage("Whats your name?");
await history.addAIMessage("Upstash");
await history.addUserMessage("Good. How are you?");
await history.addAIMessage("I'm good. Thanks.");

// eslint-disable-next-line unicorn/no-await-expression-member
let final = (await history.getMessages({ offset: 0, length: 2 })).map(
(message) => message.content as string
);
expect(["Upstash", "Good. How are you?", "I'm good. Thanks."]).toEqual(final);

// eslint-disable-next-line unicorn/no-await-expression-member
final = (await history.getMessages({ offset: 2, length: 3 })).map(
(message) => message.content as string
);
expect(["Hello!", "Hello, human.", "Whats your name?", "Upstash"]).toEqual(final);
});
19 changes: 15 additions & 4 deletions src/history/in-memory-custom-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,21 @@ export class CustomInMemoryChatMessageHistory extends BaseListChatMessageHistory
* instance.
* @returns Array of stored BaseMessage instances.
*/
async getMessages(): Promise<BaseMessage[]> {
return this.topLevelChatHistoryLength
? this.messages.slice(1).slice(-this.topLevelChatHistoryLength)
: this.messages;
async getMessages(options?: { offset?: number; length?: number }): Promise<BaseMessage[]> {
if (options) {
const start = options.offset ?? 0;
const length = options.length ?? 0;
const end = start + length + 1;

const reversedMessages = [...this.messages].reverse();
const slicedMessages = reversedMessages.slice(start, end);

return slicedMessages.reverse(); // Reverse back to original order
} else if (this.topLevelChatHistoryLength) {
return this.messages.slice(1).slice(-this.topLevelChatHistoryLength);
} else {
return this.messages;
}
}

/**
Expand Down
30 changes: 30 additions & 0 deletions src/history/redis-custom-history.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Redis } from "@upstash/redis";
import { afterAll, describe, expect, test } from "bun:test";
import { CustomUpstashRedisChatMessageHistory } from "./redis-custom-history";

describe("Redis chat-history", () => {
const redis = Redis.fromEnv();
afterAll(async () => {
await redis.flushdb();
});

test("should give last 3 messages from redis", async () => {
const history = new CustomUpstashRedisChatMessageHistory({
client: redis,
metadata: { helloWorld: "hey" },
sessionId: "testing-features",
});
await history.addUserMessage("Hello!");
await history.addAIMessage("Hello, human.");
await history.addUserMessage("Whats your name?");
await history.addAIMessage("Upstash");
await history.addUserMessage("Good. How are you?");
await history.addAIMessage("I'm good. Thanks.");

// eslint-disable-next-line unicorn/no-await-expression-member
const final = (await history.getMessages({ offset: 0, length: 2 })).map(
(message) => message.content as string
);
expect(["Upstash", "Good. How are you?", "I'm good. Thanks."]).toEqual(final);
});
});
10 changes: 6 additions & 4 deletions src/history/redis-custom-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ export class CustomUpstashRedisChatMessageHistory extends BaseListChatMessageHis
* Retrieves the chat messages from the Redis database.
* @returns An array of BaseMessage instances representing the chat history.
*/
async getMessages(): Promise<BaseMessage[]> {
const length = this.topLevelChatHistoryLength ?? [0, -1];
async getMessages(options?: { offset?: number; length?: number }): Promise<BaseMessage[]> {
const _length = options
? [options.offset ?? 0, options.length ?? -1]
: this.topLevelChatHistoryLength ?? [0, -1];

const rawStoredMessages: StoredMessage[] = await this.client.lrange<StoredMessage>(
this.sessionId,
typeof length === "number" ? 0 : length[0],
typeof length === "number" ? length : length[1]
typeof _length === "number" ? 0 : _length[0],
typeof _length === "number" ? _length : _length[1]
);

const orderedMessages = rawStoredMessages.reverse();
Expand Down
5 changes: 2 additions & 3 deletions src/rag-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,12 @@ export class RAGChat extends RAGChatBase {
}

/** Method to get history of messages used in the RAG Chat*/
getHistory(options: HistoryOptions) {
getMessageHistory(options: HistoryOptions) {
return this.historyService
.getMessageHistory({
sessionId: options.sessionId ?? DEFAULT_CHAT_SESSION_ID,
length: options.historyLength,
})
.getMessages();
.getMessages({ length: options.length, offset: options.offset });
}

/** Method to clear history of messages used in the RAG Chat*/
Expand Down
25 changes: 24 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,27 @@ export type AddContextOptions = {
namespace?: string;
};

export type HistoryOptions = Pick<ChatOptions, "historyLength" | "sessionId">;
export type HistoryOptions = {
/**
* Specifies the number of messages to get from the conversation history.
* A negative value (default is -1) means the entire conversation history will be retrieved.
* Set this to a positive number to limit the history to that many recent messages.
*
* @default -1
*/
length?: number;

/**
* Defines the number of most recent messages to skip in the conversation history.
* For example, if offset is set to 2, the two most recent messages will be skipped.
* Default value is 0, meaning no messages will be skipped.
*
* @default 0
*/
offset?: number;

/** Chat session ID of the user interacting with the application.
* @default "upstash-rag-chat-session"
*/
sessionId?: string;
};

0 comments on commit adea3ab

Please sign in to comment.