diff --git a/backend/src/chat/chat.module.ts b/backend/src/chat/chat.module.ts index 00df9643..a10a1107 100644 --- a/backend/src/chat/chat.module.ts +++ b/backend/src/chat/chat.module.ts @@ -4,7 +4,6 @@ import { ChatProxyService, ChatService } from './chat.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from 'src/user/user.model'; import { Chat } from './chat.model'; -import { Message } from 'src/chat/message.model'; import { ChatGuard } from '../guard/chat.guard'; import { AuthModule } from '../auth/auth.module'; import { UserService } from 'src/user/user.service'; @@ -12,11 +11,7 @@ import { PubSub } from 'graphql-subscriptions'; import { JwtCacheModule } from 'src/jwt-cache/jwt-cache.module'; @Module({ - imports: [ - TypeOrmModule.forFeature([Chat, User, Message]), - AuthModule, - JwtCacheModule, - ], + imports: [TypeOrmModule.forFeature([Chat, User]), AuthModule, JwtCacheModule], providers: [ ChatResolver, ChatProxyService, diff --git a/backend/src/chat/chat.service.ts b/backend/src/chat/chat.service.ts index 20f4d663..a1575437 100644 --- a/backend/src/chat/chat.service.ts +++ b/backend/src/chat/chat.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { ChatCompletionChunk, Chat } from './chat.model'; -import { Message, MessageRole } from 'src/chat/message.model'; +import { MessageRole } from 'src/chat/message.model'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from 'src/user/user.model'; @@ -46,7 +46,7 @@ export class ChatService { private userRepository: Repository, ) {} - async getChatHistory(chatId: string): Promise { + async getChatHistory(chatId: string) { const chat = await this.chatRepository.findOne({ where: { id: chatId, isDeleted: false }, }); @@ -83,7 +83,6 @@ export class ChatService { try { const messages = chat.messages || []; chat.messages = messages.filter((message: any) => !message.isDeleted); - console.log(chat); } catch (error) { console.error('Error parsing messages JSON:', error); chat.messages = []; @@ -93,31 +92,65 @@ export class ChatService { } async createChat(userId: string, newChatInput: NewChatInput): Promise { - // Fetch the user entity using the userId const user = await this.userRepository.findOne({ where: { id: userId } }); if (!user) { throw new Error('User not found'); } - // Create a new chat and associate it with the user const newChat = this.chatRepository.create({ title: newChatInput.title, messages: [], createdAt: new Date(), updatedAt: new Date(), - user: user, // Associate the user with the chat + user: user, }); return await this.chatRepository.save(newChat); } + async createChatWithMessage( + userId: string, + newChatInput: { title: string; message: string }, + ): Promise { + const user = await this.userRepository.findOne({ where: { id: userId } }); + if (!user) { + throw new Error('User not found'); + } + + // Create a new chat with the initial message + const newChat = this.chatRepository.create({ + title: newChatInput.title, + messages: [], + createdAt: new Date(), + updatedAt: new Date(), + user: user, + }); + + // Save the chat first to get an ID + const savedChat = await this.chatRepository.save(newChat); + + // Create the message with the chat's ID as a prefix + const message = { + id: `${savedChat.id}/0`, + content: newChatInput.message, + role: MessageRole.User, + createdAt: new Date(), + updatedAt: new Date(), + isActive: true, + isDeleted: false, + }; + + // Update the chat with the message + savedChat.messages = [message]; + return await this.chatRepository.save(savedChat); + } + async deleteChat(chatId: string): Promise { const chat = await this.chatRepository.findOne({ where: { id: chatId, isDeleted: false }, }); if (chat) { - // Soft delete the chat chat.isDeleted = true; chat.isActive = false; await this.chatRepository.save(chat); @@ -131,6 +164,7 @@ export class ChatService { where: { id: chatId, isDeleted: false }, }); if (chat) { + chat.messages = []; chat.updatedAt = new Date(); await this.chatRepository.save(chat); return true; @@ -153,14 +187,17 @@ export class ChatService { return null; } - async saveMessage( - chatId: string, - messageContent: string, - role: MessageRole, - ): Promise { - // Find the chat instance + async saveMessage(chatId: string, messageContent: string, role: MessageRole) { const chat = await this.chatRepository.findOne({ where: { id: chatId } }); + if (!chat) { + return null; + } + + if (!chat.messages) { + chat.messages = []; + } + // Create new message with the chat's ID as a prefix const message = { id: `${chat.id}/${chat.messages.length}`, content: messageContent, @@ -170,20 +207,16 @@ export class ChatService { isActive: true, isDeleted: false, }; - //if the chat id not exist, dont save this messages - if (!chat) { - return null; - } + chat.messages.push(message); await this.chatRepository.save(chat); - // Save the message to the database return message; } async getChatWithUser(chatId: string): Promise { return this.chatRepository.findOne({ where: { id: chatId, isDeleted: false }, - relations: ['user'], // Only load the user relation + relations: ['user'], }); } } diff --git a/backend/src/chat/message.model.ts b/backend/src/chat/message.model.ts index 4824784f..60f2628f 100644 --- a/backend/src/chat/message.model.ts +++ b/backend/src/chat/message.model.ts @@ -1,6 +1,4 @@ import { Field, ObjectType, ID, registerEnumType } from '@nestjs/graphql'; -import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; -import { SystemBaseModel } from 'src/system-base-model/system-base.model'; /** * Represents the different roles in a chat conversation @@ -30,22 +28,29 @@ registerEnumType(MessageRole, { name: 'Role', }); -@Entity() @ObjectType() -export class Message extends SystemBaseModel { - @PrimaryGeneratedColumn('uuid') +export class Message { @Field(() => ID) id: string; @Field() - @Column() content: string; @Field(() => MessageRole) - @Column({ type: 'text' }) role: MessageRole; + @Field(() => Date) + createdAt: Date; + + @Field(() => Date) + updatedAt: Date; + + @Field() + isActive: boolean; + + @Field() + isDeleted: boolean; + @Field({ nullable: true }) - @Column({ nullable: true }) modelId?: string; } diff --git a/backend/src/project/project.service.ts b/backend/src/project/project.service.ts index 5a057bbe..56eac43d 100644 --- a/backend/src/project/project.service.ts +++ b/backend/src/project/project.service.ts @@ -190,8 +190,9 @@ export class ProjectService { } // Create chat with proper title - const defaultChat = await this.chatService.createChat(userId, { + const defaultChat = await this.chatService.createChatWithMessage(userId, { title: projectName || 'New Project Chat', + message: input.description, }); // Perform the rest of project creation asynchronously @@ -550,6 +551,7 @@ export class ProjectService { } // Create default chat for the new project + // FIXME(Sma1lboy): this is not correct, we should copy chat as well const defaultChat = await this.chatService.createChat(userId, { title: `Fork of ${sourceProject.projectName}`, });