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
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ public class JobPostingIngestCommand {
private byte[] imageBytes;
private String imageContentType;
private CompanySize companySize;
private Integer candidateLimit;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
import org.springframework.web.multipart.MultipartFile;

public record JobPostingIngestMultipartRequest(String rawText, String sourceUrl, MultipartFile image,
CompanySize companySize, Integer candidateLimit) {
CompanySize companySize) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.jobdri.jobdri_api.domain.jobposting.service;

import com.jobdri.jobdri_api.domain.company.entity.Company;
import com.jobdri.jobdri_api.domain.jobposting.dto.request.JobPostingMockGenerateRequest;
import com.jobdri.jobdri_api.domain.jobposting.dto.response.JobPostingMockGenerateResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.CompletableFuture;

@Service
@RequiredArgsConstructor
public class JobPostingAiAsyncService {

private final JobPostingAiService jobPostingAiService;
private final MockQuestionCacheService mockQuestionCacheService;

@Async("llmAsyncExecutor")
public CompletableFuture<JobPostingMockGenerateResponse> generateMockJobPosting(
JobPostingMockGenerateRequest request,
Company company
) {
return CompletableFuture.completedFuture(
jobPostingAiService.generateMockJobPosting(request, company)
);
}

@Async("llmAsyncExecutor")
public CompletableFuture<List<String>> getRecommendedQuestions(JobPostingMockGenerateRequest request) {
return CompletableFuture.completedFuture(
mockQuestionCacheService.getRecommendedQuestions(request)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ private JobPostingIngestCommand snapshot(User user, JobPostingIngestMultipartReq
.imageBytes(readBytes(request.image()))
.imageContentType(readContentType(request.image()))
.companySize(request.companySize())
.candidateLimit(request.candidateLimit())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
@RequiredArgsConstructor
public class JobPostingIngestService {

private static final int DEFAULT_CANDIDATE_LIMIT = 10;
private static final int FIXED_CANDIDATE_LIMIT = 5;

@Value("${job-posting.ingest.classification-confidence-threshold:0.65}")
private double classificationConfidenceThreshold;
Expand All @@ -40,7 +40,6 @@ public JobPostingIngestResponse ingestAndCreate(User user, JobPostingIngestMulti
.rawText(request.rawText())
.sourceUrl(request.sourceUrl())
.companySize(request.companySize())
.candidateLimit(request.candidateLimit())
.build();
return ingestAndCreate(command);
}
Expand All @@ -54,9 +53,8 @@ public JobPostingIngestResponse ingestAndCreate(JobPostingIngestCommand command)
command.getSourceUrl()
);

int candidateLimit = command.getCandidateLimit() == null ? DEFAULT_CANDIDATE_LIMIT : command.getCandidateLimit();
List<JobPostingClassificationCandidateResponse> candidates =
jobPostingClassificationService.findCandidates(extracted, candidateLimit);
jobPostingClassificationService.findCandidates(extracted, FIXED_CANDIDATE_LIMIT);

if (candidates.isEmpty()) {
throw new GeneralException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.CompletableFuture;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MockJobPostingGenerationService {

private final CompanyRepository companyRepository;
private final JobPostingAiService jobPostingAiService;
private final MockQuestionCacheService mockQuestionCacheService;
private final JobPostingAiAsyncService jobPostingAiAsyncService;

public JobPostingMockGenerateResponse generate(JobPostingMockGenerateRequest request) {
Company company = companyRepository.findById(request.companyId())
Expand All @@ -26,15 +27,22 @@ public JobPostingMockGenerateResponse generate(JobPostingMockGenerateRequest req
"해당 회사를 찾을 수 없습니다. companyId=" + request.companyId()
));

JobPostingMockGenerateResponse generatedPosting = jobPostingAiService.generateMockJobPosting(request, company);
CompletableFuture<JobPostingMockGenerateResponse> generatedPostingFuture =
jobPostingAiAsyncService.generateMockJobPosting(request, company);
CompletableFuture<java.util.List<String>> recommendedQuestionsFuture =
jobPostingAiAsyncService.getRecommendedQuestions(request);

CompletableFuture.allOf(generatedPostingFuture, recommendedQuestionsFuture).join();
JobPostingMockGenerateResponse generatedPosting = generatedPostingFuture.join();

return new JobPostingMockGenerateResponse(
generatedPosting.companyName(),
generatedPosting.jobTitle(),
generatedPosting.task(),
generatedPosting.requirement(),
generatedPosting.preferred(),
generatedPosting.summary(),
mockQuestionCacheService.getRecommendedQuestions(request)
recommendedQuestionsFuture.join()
);
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/jobdri/jobdri_api/global/config/AsyncConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,22 @@ public ThreadPoolTaskExecutor mailAsyncExecutor(
executor.initialize();
return executor;
}

@Bean(name = "llmAsyncExecutor")
public ThreadPoolTaskExecutor llmAsyncExecutor(
@Value("${async.llm.core-pool-size:2}") int corePoolSize,
@Value("${async.llm.max-pool-size:4}") int maxPoolSize,
@Value("${async.llm.queue-capacity:20}") int queueCapacity
) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("llm-async-");
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setAllowCoreThreadTimeOut(true);
executor.setWaitForTasksToCompleteOnShutdown(false);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
Loading