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:프로젝트이름 생성 로직 #43

Merged
merged 4 commits into from
Dec 6, 2024
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
6 changes: 3 additions & 3 deletions app/api/v1/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
router = APIRouter()

# 이력서 생성 api
@router.post("/api/resumes", response_model=ResumeResponse)
@router.post("/api/ai/resumes", response_model=ResumeResponse)
async def generate_resume(request: ResumeRequest):
logging.basicConfig(level=logging.INFO)

Expand All @@ -39,7 +39,7 @@ async def generate_resume(request: ResumeRequest):
# aboutme_techstack = create_aboutme_techstack(project_summaries)



# 레포 이름
repo_name = "/".join(str(request.selectedRepo[0]).rstrip('/').split('/')[-2:])


Expand All @@ -62,7 +62,7 @@ async def generate_resume(request: ResumeRequest):



@router.put("/api/resumes", response_model=ResumeResponseDto)
@router.put("/api/ai/resumes", response_model=ResumeResponseDto)
async def update_resume(request: UpdateRequestDto):
try:
# 요청 데이터 확인
Expand Down
3 changes: 2 additions & 1 deletion app/config/settings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import List
from pydantic_settings import BaseSettings
from app.config.constant import GPT_MODEL, MAX_TOTAL_TOKENS, PROMPT_TOKEN_RESERVE, MAX_OUTPUT_TOKENS, MAX_CONTENT_TOKENS, DEFAULT_DATA, CODE_DATA, PR_DATA, COMMIT_DATA, PROJECT_DATA, REPO_DIRECTORY, FILE_EXTENSIONS
from app.prompts.resume_prompt import CODE_SUMMARY_PROMPT, PR_SUMMARY_PROMPT, COMMIT_DIFF_SUMMARY_PROMPT, FINAL_SUMMARY_PROMPT, FINAL_PROJECT_PROMPT, SIMPLIFY_PROJECT_PROMPT, ABOUTME_TECHSTACK_PROMPT, ABOUTME_PROMPT, RESUME_UPDATE_PROMPT
from app.prompts.resume_prompt import CODE_SUMMARY_PROMPT, PR_SUMMARY_PROMPT, COMMIT_DIFF_SUMMARY_PROMPT, FINAL_SUMMARY_PROMPT, FINAL_PROJECT_PROMPT, SIMPLIFY_PROJECT_PROMPT, ABOUTME_TECHSTACK_PROMPT, ABOUTME_PROMPT, RESUME_UPDATE_PROMPT, PROJECT_TITLE_PROMPT
class Settings(BaseSettings):
# API 키
openai_api_key: str
Expand Down Expand Up @@ -39,6 +39,7 @@ class Settings(BaseSettings):
aboutme_techstack_prompt: str = ABOUTME_TECHSTACK_PROMPT
aboutme_prompt: str = ABOUTME_PROMPT
resume_update_prompt: str = RESUME_UPDATE_PROMPT
project_title_prompt: str = PROJECT_TITLE_PROMPT

class Config:
env_file = ".env"
Expand Down
5 changes: 4 additions & 1 deletion app/dto/resume_modify_dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,7 @@ class ResumeResponseDto(BaseModel):
class UpdateRequestDto(BaseModel):
selectedText: str
requirement: str
resumeInfo: ResumeResponseDto
resumeInfo: ResumeResponseDto

class ProjectTitleDto(BaseModel):
projectTitle: str
6 changes: 6 additions & 0 deletions app/prompts/resume_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@
"Update the provided `selected_text` based on the user's request. "
"Modify only the specified text, and ensure the updated text aligns with the tone and style of the surrounding context. "
"Return only the modified text without altering the structure or other parts of the resume."
)

PROJECT_TITLE_PROMPT = (
"Return the best project title as plain text. "
"Do not include any additional explanation, formatting, or context. "
"Only provide the title itself."
)
37 changes: 37 additions & 0 deletions app/services/github_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,40 @@ def get_github_profile_and_repos(gh_token):
except Exception as e:
print(f"Error while fetching GitHub profile and repositories: {e}")
return "사용자 GitHub 프로필 요약 정보가 없습니다.", "사용자 GitHub 프로젝트 정보를 가져올 수 없습니다."

def project_title_candidate(gh_token, repo_url):
try:
# GitHub API 클라이언트 초기화
g = Github(gh_token)

# 레포지토리 이름 추출
repo_name = "/".join(repo_url.rstrip('/').split('/')[-2:])
repo = g.get_repo(repo_name)

# 제목 후보 1: 레포지토리 이름
title_candidate_1 = repo.name
print(f"Title Candidate 1 (Repo Name): {title_candidate_1}")

# 제목 후보 2: README 첫 줄
try:
readme_content = repo.get_readme().decoded_content.decode("utf-8")
title_candidate_2 = readme_content.splitlines()[0] if readme_content else ""
print(f"Title Candidate 2 (README First Line): {title_candidate_2}")
except Exception as e:
print(f"Error fetching README content: {e}")
title_candidate_2 = ""

# 제목 후보 3 : topic
try:
topics = repo.get_topics() # 토픽 리스트 가져오기
title_candidate_3 = ", ".join(topics) if topics else ""
print(f"Title Candidate 3 (Topics): {title_candidate_3}")
except Exception as e:
print(f"Error fetching topics: {e}")
title_candidate_3 = ""

return title_candidate_1, title_candidate_2, title_candidate_3

except Exception as e:
print(f"Error creating project name: {e}")
return "프로젝트 제목을 생성할 수 없습니다."
89 changes: 78 additions & 11 deletions app/services/gpt_service.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from openai import OpenAI
from app.config.settings import settings
from app.dto.resume_dto import GptProject
from app.dto.resume_modify_dto import ResumeResponseDto
from app.services.github_service import get_github_profile_and_repos
# from app.services.json_service import find_key_by_value
from app.dto.resume_modify_dto import ResumeResponseDto, ProjectTitleDto, RoleAndTaskDto
from app.services.github_service import get_github_profile_and_repos, project_title_candidate
import tiktoken
import json
import os
Expand Down Expand Up @@ -141,7 +140,6 @@ def generate_project_summary_byJson(code_summary, pr_summary, commit_summary, op
print(f"Error during summarization: {e}")
return GptProject(projectName="", skillSet="", projectDescription="")


# 최종 요약된 부분, 더 간략화 시키기 및 Json형태 포맷으로 정리
def simplify_project_summary_byJson(summary_text, openai_api_key, requirements, prompt=settings.simplify_project_prompt):
try:
Expand Down Expand Up @@ -283,13 +281,6 @@ def resume_update(openai_api_key, requirements, selected_text, context_data, pro
print("Error: User request (requirements) is empty or missing.")
return context_data # 오류 발생시 기존 데이터 반환

# # 선택된 텍스트의 키 경로 탐색
# key_path = find_key_by_value(context_data, selected_text)

# if not key_path:
# print("Error: Selected text does not match any value in the JSON data.")
# return context_data # 오류 발생시 기존 데이터 반환

# 수정 요청
print("이력서 수정")

Expand Down Expand Up @@ -339,3 +330,79 @@ def resume_update(openai_api_key, requirements, selected_text, context_data, pro
except Exception as e:
print(f"Error modifying resume with GPT: {e}")
return context_data # 오류 발생 시 기존 데이터 반환

def create_project_title(openai_api_key, gh_token, repo_url, prompt=settings.project_title_prompt):
try:
# 제목 후보 가져오기
title_candidates = project_title_candidate(gh_token, repo_url)
title_candidate_1 = title_candidates.get("title_candidate_1", "")
title_candidate_2 = title_candidates.get("title_candidate_2", "")
title_candidate_3 = title_candidates.get("title_candidate_3", "")

# 후보 검증 및 최종 제목 결정
if title_candidate_1 == title_candidate_2:
print("Title Candidate 1 and 2 are identical.")
title = {"projectTitle": title_candidate_1}
return title

print("프로젝트 제목을 추론합니다.")

# OpenAI API 호출
client = OpenAI(api_key=openai_api_key)
response = client.beta.chat.completions.parse(
model=settings.gpt_model,
messages=[
{
"role": "system",
"content": (
"You are a professional assistant tasked with deciding the best project title. "
"Evaluate the provided title candidates and choose the most appropriate one. "
"Focus on clarity, relevance, and alignment with typical project naming conventions."
)
},
{
"role": "user",
"content": (
"You are tasked with evaluating and determining the best project title based on the following candidates:\n\n"
f"title_candidate_1 (Repository Name):\n{title_candidate_1}\n\n"
f"title_candidate_2 (README First Line):\n{title_candidate_2}\n\n"
f"title_candidate_3 (Topics):\n{title_candidate_3}\n\n"
"Candidate 1 and 2 are the primary sources for the project title as they are likely to directly reflect the project’s purpose. "
"Evaluate these first to infer a suitable title. "
"If these candidates lack sufficient clarity or relevance, use Candidate 3 (Topics) to provide additional context or inspiration for the title.\n\n"
"Your decision should prioritize the following criteria:\n"
"- Clarity: The title should be easy to understand and intuitive.\n"
"- Relevance: The title should accurately represent the project’s purpose, functionality, or key features.\n"
"- Alignment: The title should align with common naming conventions for projects of this type.\n\n"
"If none of the candidates are appropriate, synthesize information from all three candidates to create a new title that best fits the project.\n\n"
"Be concise and professional in your suggestion."
)
},
{
"role": "assistant",
"content": f"Sample summary format: {prompt}."
}
],
max_tokens=settings.max_output_tokens,
response_format=ProjectTitleDto
)
# GPT 응답 파싱
response_text = response.choices[0].message.parsed

# 최종 리턴값 확인 - 테스트용
print(f"Final Response Text: {response_text}")

# ProjectTitleDto 객체로 반환
return response_text


except Exception as e:
print(f"Error modifying resume with GPT: {e}")
return {
"projectTitle": "",
"title_candidates": {
"title_candidate_1": "",
"title_candidate_2": "",
"title_candidate_3": ""
}
}
Loading