-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(lightspeed): add backend config and /conversations/:conversation…
…_id GET&DELETE API (#2211) * add backend config and load session API and tests Signed-off-by: Stephanie <yangcao@redhat.com> * generate dist-dynamic Signed-off-by: Stephanie <yangcao@redhat.com> * fix tsc Signed-off-by: Stephanie <yangcao@redhat.com> * update to conversations endpoint with path params Signed-off-by: Stephanie <yangcao@redhat.com> * run yarn tsc Signed-off-by: Stephanie <yangcao@redhat.com> * add delete API Signed-off-by: Stephanie <yangcao@redhat.com> * resolve review comments Signed-off-by: Stephanie <yangcao@redhat.com> --------- Signed-off-by: Stephanie <yangcao@redhat.com>
- Loading branch information
Showing
10 changed files
with
438 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,25 @@ | ||
export interface Config {} | ||
export interface Config { | ||
/** | ||
* Configuration required for using lightspeed | ||
* @visibility frontend | ||
*/ | ||
lightspeed: { | ||
servers: Array<{ | ||
/** | ||
* The id of the server. | ||
* @visibility frontend | ||
*/ | ||
id: string; | ||
/** | ||
* The url of the server. | ||
* @visibility frontend | ||
*/ | ||
url: string; | ||
/** | ||
* The access token for authenticating server. | ||
* @visibility secret | ||
*/ | ||
token?: string; | ||
}>; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
plugins/lightspeed-backend/src/handlers/chatHistory.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { AIMessage, HumanMessage } from '@langchain/core/messages'; | ||
|
||
import { Roles } from '../service/types'; | ||
import { deleteHistory, loadHistory, saveHistory } from './chatHistory'; | ||
|
||
const mockConversationId = 'user1+1q2w3e4r-qwer1234'; | ||
|
||
describe('Test History Functions', () => { | ||
afterEach(async () => { | ||
// Clear the history store before each test | ||
await deleteHistory(mockConversationId); | ||
}); | ||
|
||
test('saveHistory should save a human message', async () => { | ||
const message = 'Hello, how are you?'; | ||
|
||
await saveHistory(mockConversationId, Roles.HumanRole, message); | ||
|
||
const history = await loadHistory(mockConversationId, 10); | ||
expect(history.length).toBe(1); | ||
expect(history[0]).toBeInstanceOf(HumanMessage); | ||
expect(history[0].content).toBe(message); | ||
}); | ||
|
||
test('saveHistory should save an AI message', async () => { | ||
const message = 'I am fine, thank you!'; | ||
|
||
await saveHistory(mockConversationId, Roles.AIRole, message); | ||
|
||
const history = await loadHistory(mockConversationId, 10); | ||
|
||
expect(history.length).toBe(1); | ||
expect(history[0]).toBeInstanceOf(AIMessage); | ||
expect(history[0].content).toBe(message); | ||
}); | ||
|
||
test('saveHistory and loadHistory with multiple messages', async () => { | ||
await saveHistory(mockConversationId, Roles.HumanRole, 'Hello'); | ||
await saveHistory( | ||
mockConversationId, | ||
Roles.AIRole, | ||
'Hi! How can I help you today?', | ||
); | ||
|
||
const history = await loadHistory(mockConversationId, 10); | ||
expect(history.length).toBe(2); | ||
expect(history[0]).toBeInstanceOf(HumanMessage); | ||
expect(history[0].content).toBe('Hello'); | ||
expect(history[1]).toBeInstanceOf(AIMessage); | ||
expect(history[1].content).toBe('Hi! How can I help you today?'); | ||
}); | ||
|
||
test('saveHistory and loadHistory with exact number of messages', async () => { | ||
await saveHistory(mockConversationId, Roles.HumanRole, 'Hello'); | ||
await saveHistory( | ||
mockConversationId, | ||
Roles.AIRole, | ||
'Hi! How can I help you today?', | ||
); | ||
|
||
const history = await loadHistory(mockConversationId, 1); | ||
expect(history.length).toBe(1); | ||
expect(history[0]).toBeInstanceOf(AIMessage); | ||
expect(history[0].content).toBe('Hi! How can I help you today?'); | ||
}); | ||
|
||
test('saveHistory should throw an error for unknown roles', async () => { | ||
await expect( | ||
saveHistory(mockConversationId, 'UnknownRole', 'Message'), | ||
).rejects.toThrow('Unknown role: UnknownRole'); | ||
}); | ||
|
||
test('deleteHistory should delete specific conversation', async () => { | ||
await saveHistory(mockConversationId, Roles.HumanRole, 'Hello'); | ||
await saveHistory('conv2', Roles.AIRole, 'Hi! How can I help you today?'); | ||
|
||
const history1 = await loadHistory(mockConversationId, 1); | ||
expect(history1.length).toBe(1); | ||
|
||
const history2 = await loadHistory('conv2', 1); | ||
expect(history2.length).toBe(1); | ||
|
||
await deleteHistory('conv2'); | ||
|
||
await expect(loadHistory('conv2', 1)).rejects.toThrow( | ||
'unknown conversation_id: conv2', | ||
); | ||
|
||
expect(await loadHistory(mockConversationId, 1)).toBeDefined(); | ||
}); | ||
|
||
test('deleteHistory should not return error with unknown id', async () => { | ||
await expect(() => deleteHistory(mockConversationId)).not.toThrow(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { | ||
AIMessage, | ||
BaseMessage, | ||
HumanMessage, | ||
SystemMessage, | ||
} from '@langchain/core/messages'; | ||
import { InMemoryStore } from '@langchain/core/stores'; | ||
|
||
import { Roles } from '../service/types'; | ||
|
||
const historyStore = new InMemoryStore<BaseMessage[]>(); | ||
|
||
export async function saveHistory( | ||
conversation_id: string, | ||
role: string, | ||
message: string, | ||
): Promise<void> { | ||
let newMessage: BaseMessage; | ||
switch (role) { | ||
case Roles.AIRole: { | ||
newMessage = new AIMessage(message); | ||
break; | ||
} | ||
case Roles.HumanRole: { | ||
newMessage = new HumanMessage(message); | ||
break; | ||
} | ||
case Roles.SystemRole: { | ||
newMessage = new SystemMessage(message); | ||
break; | ||
} | ||
default: | ||
throw new Error(`Unknown role: ${role}`); | ||
} | ||
|
||
const sessionHistory = await historyStore.mget([conversation_id]); | ||
let newHistory: BaseMessage[] = []; | ||
if (sessionHistory && sessionHistory[0]) { | ||
newHistory = sessionHistory[0]; | ||
} | ||
newHistory.push(newMessage); | ||
await historyStore.mset([[conversation_id, newHistory]]); | ||
} | ||
|
||
export async function loadHistory( | ||
conversation_id: string, | ||
historyLength: number, | ||
): Promise<BaseMessage[]> { | ||
const sessionHistory = await historyStore.mget([conversation_id]); | ||
if (!sessionHistory[0]) { | ||
throw new Error(`unknown conversation_id: ${conversation_id}`); | ||
} | ||
return sessionHistory[0]?.slice(-historyLength); | ||
} | ||
|
||
export async function deleteHistory(conversation_id: string): Promise<void> { | ||
return await historyStore.mdelete([conversation_id]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.