Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: getting project by chatId and show load status in code engine #119

Merged
merged 6 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 4 additions & 0 deletions backend/src/chat/chat.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export class Chat extends SystemBaseModel {
@JoinColumn({ name: 'user_id' })
@Field(() => User)
user: User;

@ManyToOne(() => Project, (project) => project.chats)
@Field(() => Project, { nullable: true })
project: Project;
}

@ObjectType('ChatCompletionDeltaType')
Expand Down
18 changes: 13 additions & 5 deletions backend/src/chat/chat.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable, Logger } from '@nestjs/common';
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { ChatCompletionChunk, Chat } from './chat.model';
import { Message, MessageRole } from 'src/chat/message.model';
import { InjectRepository } from '@nestjs/typeorm';
Expand Down Expand Up @@ -73,12 +73,20 @@ export class ChatService {
async getChatDetails(chatId: string): Promise<Chat> {
const chat = await this.chatRepository.findOne({
where: { id: chatId, isDeleted: false },
relations: ['messages'],
relations: ['project'],
});

if (chat) {
// Filter out messages that are soft-deleted
chat.messages = chat.messages.filter((message) => !message.isDeleted);
if (!chat) {
throw new NotFoundException('Chat not found');
}

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 = [];
}

return chat;
Expand Down
8 changes: 7 additions & 1 deletion backend/src/project/project-packages.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export class ProjectPackages extends SystemBaseModel {
@PrimaryGeneratedColumn()
id: string;

@Field()
@Column({ nullable: false })
name: string;

@Field()
@Column('text')
content: string;
Expand All @@ -18,6 +22,8 @@ export class ProjectPackages extends SystemBaseModel {
@Column()
version: string;

@ManyToMany(() => Project, (project) => project.projectPackages)
@ManyToMany(() => Project, (project) => project.projectPackages, {
nullable: true,
})
projects: Project[];
}
8 changes: 5 additions & 3 deletions backend/src/project/project.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
JoinColumn,
ManyToMany,
JoinTable,
OneToMany,
} from 'typeorm';
import { User } from 'src/user/user.model';
import { ProjectPackages } from './project-packages.model';
Expand Down Expand Up @@ -58,10 +59,11 @@ export class Project extends SystemBaseModel {
})
projectPackages: ProjectPackages[];

@Field(() => [Chat], { nullable: true })
@Field(() => [Chat])
@OneToMany(() => Chat, (chat) => chat.project, {
cascade: true,
eager: false,
cascade: true, // Automatically save related chats
lazy: true, // Load chats only when accessed
onDelete: 'CASCADE', // Delete chats when user is deleted
})
chats: Chat[];
}
7 changes: 5 additions & 2 deletions backend/src/project/project.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import { ProjectService } from './project.service';
import { ProjectsResolver } from './project.resolver';
import { AuthModule } from '../auth/auth.module';
import { ProjectGuard } from '../guard/project.guard';
import { ChatService } from 'src/chat/chat.service';
import { User } from 'src/user/user.model';
import { Chat } from 'src/chat/chat.model';

@Module({
imports: [
TypeOrmModule.forFeature([Project, ProjectPackages]),
TypeOrmModule.forFeature([Project, Chat, User, ProjectPackages]),
AuthModule, // Import AuthModule to provide JwtService to the ProjectGuard
],
providers: [ProjectService, ProjectsResolver, ProjectGuard],
providers: [ChatService, ProjectService, ProjectsResolver, ProjectGuard],
exports: [ProjectService, ProjectGuard],
})
export class ProjectModule {}
23 changes: 14 additions & 9 deletions backend/src/project/project.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ import { CreateProjectInput, IsValidProjectInput } from './dto/project.input';
import { UseGuards } from '@nestjs/common';
import { ProjectGuard } from '../guard/project.guard';
import { GetUserIdFromToken } from '../decorator/get-auth-token.decorator';
import { User } from '../user/user.model';
import { Chat } from '../chat/chat.model';
import { Chat } from 'src/chat/chat.model';

@Resolver(() => Project)
export class ProjectsResolver {
constructor(private readonly projectsService: ProjectService) {}

@Query(() => [Project])
async getProjects(@GetUserIdFromToken() userId: string): Promise<Project[]> {
async getUserProjects(
@GetUserIdFromToken() userId: string,
): Promise<Project[]> {
return this.projectsService.getProjectsByUser(userId);
}

Expand All @@ -31,12 +32,16 @@ export class ProjectsResolver {
return this.projectsService.getProjectById(projectId);
}

@Mutation(() => Project)
async createPorject(
@GetUserIdFromToken() userId: number,
@Mutation(() => Chat)
async createProject(
@GetUserIdFromToken() userId: string,
@Args('createProjectInput') createProjectInput: CreateProjectInput,
): Promise<Project> {
return this.projectsService.createProject(createProjectInput, userId);
): Promise<Chat> {
const resChat = await this.projectsService.createProject(
createProjectInput,
userId,
);
return resChat;
}

@Mutation(() => Boolean)
Expand All @@ -47,7 +52,7 @@ export class ProjectsResolver {

@Query(() => Boolean)
async isValidateProject(
@GetUserIdFromToken() userId: number,
@GetUserIdFromToken() userId: string,
@Args('isValidProject') input: IsValidProjectInput,
): Promise<boolean> {
return this.projectsService.isValidProject(userId, input);
Expand Down
148 changes: 97 additions & 51 deletions backend/src/project/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
import { OpenAIModelProvider } from 'src/common/model-provider/openai-model-provider';
import { MessageRole } from 'src/chat/message.model';
import { BuilderContext } from 'src/build-system/context';
import { ChatService } from 'src/chat/chat.service';
import { Chat } from 'src/chat/chat.model';

@Injectable()
export class ProjectService {
Expand All @@ -30,8 +32,11 @@ export class ProjectService {
constructor(
@InjectRepository(Project)
private projectsRepository: Repository<Project>,
@InjectRepository(Chat)
private chatRepository: Repository<Chat>,
@InjectRepository(ProjectPackages)
private projectPackagesRepository: Repository<ProjectPackages>,
private chatService: ChatService,
) {}

async getProjectsByUser(userId: string): Promise<Project[]> {
Expand Down Expand Up @@ -79,58 +84,98 @@ export class ProjectService {
}
return project;
}
// staring build the project
async createProject(
input: CreateProjectInput,
userId: number,
): Promise<Project> {
if (input.projectName === '') {
this.logger.debug(
'Project name not exist in input, generating project name',
);
const nameGenerationPrompt = await generateProjectNamePrompt(
input.description,
);
const response = await this.model.chatSync({
messages: [
{
role: MessageRole.System,
content:
'You are a specialized project name generator. Respond only with the generated name.',
},
{
role: MessageRole.User,
content: nameGenerationPrompt,
},
],
});
input.projectName = response;
this.logger.debug(`Generated project name: ${input.projectName}`);
}
// Build project sequence and get project path
const sequence = buildProjectSequenceByProject(input);
const context = new BuilderContext(sequence, sequence.id);
const projectPath = await context.execute();

// Create new project entity
const project = new Project();
project.projectName = input.projectName;
project.projectPath = projectPath;
project.userId = userId;

// Transform input packages to ProjectPackages entities
const projectPackages = await this.transformInputToProjectPackages(
input.packages,
);
project.projectPackages = projectPackages;

// binding project and chats
async bindProjectAndChat(project: Project, chat: Chat): Promise<boolean> {
await this.projectsRepository.manager.connection.synchronize();
await this.chatRepository.manager.connection.synchronize();
if (!chat) {
this.logger.error('chat is undefined');
return false;
}
try {
return await this.projectsRepository.save(project);
chat.project = project;
if (!project.chats) {
project.chats = [];
}
const chatArray = await project.chats;
chatArray.push(chat);
console.log(chat);
console.log(project);
await this.projectsRepository.save(project);
await this.chatRepository.save(chat);

return true;
} catch (error) {
this.logger.error('Error creating project:', error);
throw new InternalServerErrorException('Error creating the project.');
console.error('Error binding project and chat:', error);
return false;
}
}

async createProject(
input: CreateProjectInput,
userId: string,
): Promise<Chat> {
const defaultChatPromise = await this.chatService.createChat(userId, {
title: input.projectName || 'Default Project Chat',
});

const projectPromise = (async () => {
try {
const nameGenerationPrompt = await generateProjectNamePrompt(
input.description,
);
const response = await this.model.chatSync({
model: 'gpt-4o',
messages: [
{
role: MessageRole.System,
content:
'You are a specialized project name generator. Respond only with the generated name.',
},
{
role: MessageRole.User,
content: nameGenerationPrompt,
},
],
});

if (input.projectName === '') {
this.logger.debug(
'Project name not exist in input, generating project name',
);
input.projectName = response;
this.logger.debug(`Generated project name: ${input.projectName}`);
}

const sequence = buildProjectSequenceByProject(input);
const context = new BuilderContext(sequence, sequence.id);
const projectPath = await context.execute();

const project = new Project();
project.projectName = input.projectName;
project.projectPath = projectPath;
project.userId = userId;

project.projectPackages = await this.transformInputToProjectPackages(
input.packages,
);

const savedProject = await this.projectsRepository.save(project);

const defaultChat = await defaultChatPromise;
await this.bindProjectAndChat(savedProject, defaultChat);

console.log('Binded project and chats');
return savedProject;
} catch (error) {
this.logger.error('Error creating project:', error);
throw new InternalServerErrorException('Error creating the project.');
}
})();

return defaultChatPromise;
}
private async transformInputToProjectPackages(
inputPackages: ProjectPackage[],
): Promise<ProjectPackages[]> {
Expand Down Expand Up @@ -173,9 +218,10 @@ export class ProjectService {
return transformedPackages;
} catch (error) {
this.logger.error('Error transforming packages:', error);
throw new InternalServerErrorException(
'Error processing project packages.',
);
// throw new InternalServerErrorException(
// 'Error processing project packages.',
// );
return Promise.resolve([]);
}
}

Expand Down Expand Up @@ -213,7 +259,7 @@ export class ProjectService {
}

async isValidProject(
userId: number,
userId: string,
input: IsValidProjectInput,
): Promise<boolean> {
try {
Expand Down
Loading
Loading