Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/backend-dev-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,11 +1707,12 @@
"description": "The optional rating for the whole conversation.",
"enum": ["good", "bad", "unrated"]
},
"updatedAt": { "type": "string", "description": "The time of the last update.", "format": "date" },
"createdAt": { "type": "string", "description": "The creation time.", "format": "date" },
"context": { "type": "object", "description": "The context values." },
"extensionUserArguments": { "type": "object", "description": "The argument values." }
},
"required": ["id", "configurationId", "createdAt"]
"required": ["id", "configurationId", "updatedAt", "createdAt"]
},
"ConversationsDto": {
"type": "object",
Expand Down
9 changes: 9 additions & 0 deletions backend/src/controllers/conversations/dtos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,14 @@ export class ConversationDto {
})
rating?: ConversationRating;

@ApiProperty({
description: 'The time of the last update.',
required: true,
type: String,
format: 'date',
})
updatedAt!: Date;

@ApiProperty({
description: 'The creation time.',
required: true,
Expand Down Expand Up @@ -302,6 +310,7 @@ export class ConversationDto {
static fromDomain(this: void, source: Conversation) {
const result = new ConversationDto();
result.id = source.id;
result.updatedAt = source.updatedAt;
result.createdAt = source.createdAt;
result.configurationId = source.configurationId;
result.name = source.name;
Expand Down
1 change: 1 addition & 0 deletions backend/src/domain/chat/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface Conversation {

readonly rating?: ConversationRating;

readonly updatedAt: Date;
readonly createdAt: Date;

// The chosen llm.
Expand Down
25 changes: 23 additions & 2 deletions backend/src/domain/chat/middlewares/get-history-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { onErrorResumeNextWith } from 'rxjs';
import { In } from 'typeorm';
import { ExtensionSource, MessageEntity, MessageRepository } from 'src/domain/database';
import {
ConversationEntity,
ConversationRepository,
ExtensionSource,
MessageEntity,
MessageRepository,
} from 'src/domain/database';
import { ConversationFileEntity, ConversationFileRepository } from '../../database/entities/conversation-file';
import {
AIMessage,
Expand All @@ -23,6 +29,8 @@ export class GetHistoryMiddleware implements ChatMiddleware {
constructor(
@InjectRepository(MessageEntity)
private readonly messages: MessageRepository,
@InjectRepository(ConversationEntity)
private readonly conversations: ConversationRepository,
@InjectRepository(ConversationFileEntity)
private readonly conversationFiles: ConversationFileRepository,
) {}
Expand All @@ -32,7 +40,14 @@ export class GetHistoryMiddleware implements ChatMiddleware {
async invoke(context: ChatContext, getContext: GetContext, next: ChatNextDelegate): Promise<any> {
const { conversationId, configuration } = context;

const history = new InternalChatHistory(conversationId, configuration.id, context, this.messages, this.conversationFiles);
const history = new InternalChatHistory(
conversationId,
configuration.id,
context,
this.messages,
this.conversations,
this.conversationFiles,
);

await history.addMessage(new HumanMessage(context.input), true, context.editMessageId);

Expand All @@ -54,6 +69,7 @@ class InternalChatHistory extends MessagesHistory {
private readonly configurationId: number,
private readonly context: ChatContext,
private readonly messages: MessageRepository,
private readonly conversations: ConversationRepository,
private readonly conversationFiles: ConversationFileRepository,
) {
super();
Expand Down Expand Up @@ -196,6 +212,11 @@ class InternalChatHistory extends MessagesHistory {
await this.attachNewFilesToConversation(entity.conversationId, entity.id, this.context.files);
this.context.result.next({ type: 'saved', messageId: entity.id, messageType: 'human' });
}

// also mark the conversation as updated
const conversation = await this.conversations.findOneByOrFail({ id: this.conversationId });
conversation.updatedAt = new Date();
await this.conversations.save(conversation);
} catch (err) {
this.logger.error('Failed to store message in history.', err);
}
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/api/generated/models/ConversationDto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export interface ConversationDto {
* @memberof ConversationDto
*/
rating?: ConversationDtoRatingEnum;
/**
* The time of the last update.
* @type {Date}
* @memberof ConversationDto
*/
updatedAt: Date;
/**
* The creation time.
* @type {Date}
Expand Down Expand Up @@ -82,6 +88,7 @@ export type ConversationDtoRatingEnum = typeof ConversationDtoRatingEnum[keyof t
export function instanceOfConversationDto(value: object): value is ConversationDto {
if (!('id' in value) || value['id'] === undefined) return false;
if (!('configurationId' in value) || value['configurationId'] === undefined) return false;
if (!('updatedAt' in value) || value['updatedAt'] === undefined) return false;
if (!('createdAt' in value) || value['createdAt'] === undefined) return false;
return true;
}
Expand All @@ -100,6 +107,7 @@ export function ConversationDtoFromJSONTyped(json: any, ignoreDiscriminator: boo
'name': json['name'] == null ? undefined : json['name'],
'configurationId': json['configurationId'],
'rating': json['rating'] == null ? undefined : json['rating'],
'updatedAt': (new Date(json['updatedAt'])),
'createdAt': (new Date(json['createdAt'])),
'context': json['context'] == null ? undefined : json['context'],
'extensionUserArguments': json['extensionUserArguments'] == null ? undefined : json['extensionUserArguments'],
Expand All @@ -116,6 +124,7 @@ export function ConversationDtoToJSON(value?: ConversationDto | null): any {
'name': value['name'],
'configurationId': value['configurationId'],
'rating': value['rating'],
'updatedAt': ((value['updatedAt']).toISOString().substring(0,10)),
'createdAt': ((value['createdAt']).toISOString().substring(0,10)),
'context': value['context'],
'extensionUserArguments': value['extensionUserArguments'],
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/pages/chat/ConversationItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ function groupConversations(chats: ConversationDto[]): ConversationGroup[] {
return [];
}

chats = [...chats].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());

const now = new Date();
const withinWeek = startOfDay(addDays(now, -7));
const withinMonth = startOfDay(addDays(now, -30));
Expand All @@ -19,7 +21,7 @@ function groupConversations(chats: ConversationDto[]): ConversationGroup[] {
const conversationGroups = Array.from(
chats
.reduce((prev, curr) => {
const date = curr.createdAt;
const date = curr.updatedAt;

let group: { label: string; date: Date };
if (isSameDay(now, date)) {
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/pages/chat/Conversations.ui-unit.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ describe('Conversations', () => {
const now = new Date();

const mockedChats: ConversationDto[] = [
{ id: 1, createdAt: now, configurationId: 1 },
{ id: 4, createdAt: new Date('2023-10-01'), configurationId: 1 },
{ id: 3, createdAt: startOfDay(addDays(now, -20)), configurationId: 1 },
{ id: 2, createdAt: startOfDay(addDays(now, -2)), configurationId: 1 },
{ id: 5, createdAt: new Date('2022-10-01'), configurationId: 1 },
{ id: 1, updatedAt: now, createdAt: now, configurationId: 1 },
{ id: 4, updatedAt: new Date('2023-10-01'), createdAt: new Date('2023-10-01'), configurationId: 1 },
{ id: 3, updatedAt: startOfDay(addDays(now, -20)), createdAt: startOfDay(addDays(now, -20)), configurationId: 1 },
{ id: 2, updatedAt: startOfDay(addDays(now, -2)), createdAt: startOfDay(addDays(now, -2)), configurationId: 1 },
{ id: 5, updatedAt: new Date('2022-10-01'), createdAt: new Date('2022-10-01'), configurationId: 1 },
];

it('should render conversations components sorted by date', () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/chat/state/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ export const useStateOfChat = () => {
const currentChatId = useChatStore((s) => s.currentChatId);
const chatDataMap = useChatStore((s) => s.chatDataMap);
const chatData = chatDataMap.get(currentChatId);
return chatData?.chat || { id: 0, configurationId: -1, createdAt: new Date() };
return chatData?.chat || { id: 0, configurationId: -1, rating: null, updatedAt: new Date(), createdAt: new Date() };
};

export const useStateOfSelectedChatId = () => useChatStore((s) => s.currentChatId);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/chat/state/zustand/chatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type ChatActions = {

const createEmptyChatData = (chatId: number): ChatData => ({
messages: [],
chat: { id: chatId, configurationId: -1, createdAt: new Date() },
chat: { id: chatId, configurationId: -1, updatedAt: new Date(), createdAt: new Date() },
isAiWriting: false,
activeStreamSubscription: undefined,
streamingMessageId: undefined,
Expand Down