Skip to content

Commit

Permalink
feat: getting project by chatId and show load status in code engine
Browse files Browse the repository at this point in the history
  • Loading branch information
NarwhalChen committed Feb 8, 2025
1 parent 00f2df6 commit f3521c5
Show file tree
Hide file tree
Showing 21 changed files with 677 additions and 187 deletions.
5 changes: 5 additions & 0 deletions backend/src/chat/chat.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { Message } from 'src/chat/message.model';
import { SystemBaseModel } from 'src/system-base-model/system-base.model';
import { User } from 'src/user/user.model';
import { Project } from 'src/project/project.model';

export enum StreamStatus {
STREAMING = 'streaming',
Expand Down Expand Up @@ -44,6 +45,10 @@ export class Chat extends SystemBaseModel {
@ManyToOne(() => User, (user) => user.chats)
@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[];
}
12 changes: 11 additions & 1 deletion backend/src/project/project.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {
JoinColumn,
ManyToMany,
JoinTable,
OneToMany,
} from 'typeorm';
import { User } from 'src/user/user.model';
import { ProjectPackages } from './project-packages.model';
import { Chat } from 'src/chat/chat.model';

@Entity()
@ObjectType()
Expand All @@ -29,7 +31,7 @@ export class Project extends SystemBaseModel {

@Field(() => ID)
@Column()
userId: number;
userId: string;

@ManyToOne(() => User)
@JoinColumn({ name: 'user_id' })
Expand All @@ -52,4 +54,12 @@ export class Project extends SystemBaseModel {
},
})
projectPackages: ProjectPackages[];

@Field(() => [Chat])
@OneToMany(() => Chat, (chat) => chat.project, {
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 {}
19 changes: 12 additions & 7 deletions backend/src/project/project.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ 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 { Chat } from 'src/chat/chat.model';

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

@Query(() => [Project])
async getUserProjects(
@GetUserIdFromToken() userId: number,
@GetUserIdFromToken() userId: string,
): Promise<Project[]> {
return this.projectsService.getProjectsByUser(userId);
}
Expand All @@ -27,12 +28,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 @@ -43,7 +48,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
146 changes: 95 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,11 +32,14 @@ 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: number): Promise<Project[]> {
async getProjectsByUser(userId: string): Promise<Project[]> {
const projects = await this.projectsRepository.find({
where: { userId: userId, isDeleted: false },
relations: ['projectPackages'],
Expand Down Expand Up @@ -70,58 +75,96 @@ export class ProjectService {
return project;
}

// staring build the project
// 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 {
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) {
console.error('Error binding project and chat:', error);
return false;
}
}

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}`);
}
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,
},
],
});

// Build project sequence and get project path
const sequence = buildProjectSequenceByProject(input);
const context = new BuilderContext(sequence, sequence.id);
const projectPath = await context.execute();
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}`);
}

// Create new project entity
const project = new Project();
project.projectName = input.projectName;
project.projectPath = projectPath;
project.userId = userId;
const sequence = buildProjectSequenceByProject(input);
const context = new BuilderContext(sequence, sequence.id);
const projectPath = await context.execute();

// Transform input packages to ProjectPackages entities
const projectPackages = await this.transformInputToProjectPackages(
input.packages,
);
project.projectPackages = projectPackages;
const project = new Project();
project.projectName = input.projectName;
project.projectPath = projectPath;
project.userId = userId;

try {
return await this.projectsRepository.save(project);
} catch (error) {
this.logger.error('Error creating project:', error);
throw new InternalServerErrorException('Error creating the project.');
}
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(
Expand Down Expand Up @@ -166,9 +209,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 @@ -203,7 +247,7 @@ export class ProjectService {
}

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

0 comments on commit f3521c5

Please sign in to comment.