Skip to content

Commit

Permalink
process history
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolk committed Sep 25, 2024
1 parent f98896c commit 767e73b
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { GraphqlRequestExecutor } from './graphql_request_executor';
export type ConversationHistoryMessageItem = ConversationMessage & {
id: string;
conversationId: string;
associatedUserMessageId?: string;
aiContext?: unknown;
};

export type GetQueryInput = {
Expand Down Expand Up @@ -40,6 +42,8 @@ export type ListQueryOutput = {
const messageItemSelectionSet = `
id
conversationId
associatedUserMessageId
aiContext
role
content {
text
Expand Down Expand Up @@ -121,6 +125,61 @@ export class ConversationMessageHistoryRetriever {
messages.push(currentMessage);
}

// Index assistant messages by corresponding user message.
const assistantMessageByUserMessageId: Map<
string,
ConversationHistoryMessageItem
> = new Map();
messages.forEach((message) => {
if (message.role === 'assistant' && message.associatedUserMessageId) {
assistantMessageByUserMessageId.set(
message.associatedUserMessageId,
message
);
}
});

// Reconcile history and inject aiContext
messages.reduce((acc, current) => {
// Bedrock expects that message history is user->assistant->user->assistant->... and so on.
// The chronological order doesn't assure this ordering if there were any concurrent messages sent.
// Therefore, conversation is ordered by user's messages only and corresponding assistant messages are inserted
// into right place regardless of their createdAt value.
// This algorithm assumes that GQL query returns messages sorted by createdAt.
if (current.role === 'assistant') {
// Initially, skip assistant messages, these might be out of chronological order.
return acc;
}
if (
current.role === 'user' &&
!assistantMessageByUserMessageId.has(current.id) &&
current.id !== this.event.currentMessageId
) {
// Skip user messages that didn't get answer from assistant yet.
// These might be still "in-flight", i.e. assistant is still working on them in separate invocation.
// Except current message, we want to process that one.
return acc;
}
const aiContext = current.aiContext;
const content = aiContext
? [...current.content, { text: JSON.stringify(aiContext) }]
: current.content;

acc.push({ role: current.role, content });

// Find and insert corresponding assistant message.
const correspondingAssistantMessage = assistantMessageByUserMessageId.get(
current.id
);
if (correspondingAssistantMessage) {
acc.push({
role: correspondingAssistantMessage.role,
content: correspondingAssistantMessage.content,
});
}
return acc;
}, [] as Array<ConversationMessage>);

return messages;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type ConversationTurnAppSyncResponse = {
type CreateConversationMessageChatInput = ConversationMessage & {
conversationId: string;
id: string;
associatedUserMessageId?: string;
};

const commonEventProperties = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const schema = a.schema({
ConversationMessageChat: a
.model({
conversationId: a.id(),
associatedUserMessageId: a.id(),
role: a.ref('MockConversationParticipantRole'),
content: a.ref('MockContentBlock').array(),
aiContext: a.json(),
Expand Down

0 comments on commit 767e73b

Please sign in to comment.