Skip to content
Open
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
29 changes: 29 additions & 0 deletions myeongsung/app/services/experience_merge_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,32 @@ def apply_merge_results_to_step2(
experience.merge_similarity = result.similarity

return step2_experiences


def apply_sequential_merge_results_to_step2(
step2_experiences: List[dict],
existing_experiences: List[MergeExperiencePayload],
threshold: Optional[float] = None,
embedding_client: Optional[Any] = None,
) -> List[dict]:
accepted_candidates: List[Any] = list(existing_experiences)

for index, experience in enumerate(step2_experiences):
merge_response = check_merge_candidates(
targets=[experience],
existing_experiences=accepted_candidates,
threshold=threshold,
top_k=1,
embedding_client=embedding_client,
)
result = merge_response.results[0]
experience["needs_merge"] = result.needs_merge
experience["merge_candidate_id"] = result.merge_candidate_id
experience["merge_similarity"] = result.similarity

if not result.needs_merge:
accepted_candidate = dict(experience)
accepted_candidate["id"] = f"batch:{index}"
accepted_candidates.append(accepted_candidate)

return step2_experiences
49 changes: 48 additions & 1 deletion myeongsung/tests/test_experience_merge_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
import unittest

from app.schemas.resume_dto import MergeExperiencePayload, Step2ExtractedExperience
from app.services.experience_merge_service import build_embedding_text, check_merge_candidates
from app.services.experience_merge_service import (
apply_sequential_merge_results_to_step2,
build_embedding_text,
check_merge_candidates,
)


class FakeEmbeddings:
Expand Down Expand Up @@ -34,6 +38,20 @@ class LowSimilarityOpenAI:
embeddings = LowSimilarityEmbeddings()


class SameEmbeddings:
def create(self, model, input):
return SimpleNamespace(
data=[
SimpleNamespace(embedding=[1.0, 0.0])
for _ in input
]
)


class SameOpenAI:
embeddings = SameEmbeddings()


class ExperienceMergeServiceTest(unittest.TestCase):

def test_check_merge_candidates_marks_similar_target(self):
Expand Down Expand Up @@ -185,6 +203,35 @@ def test_low_embedding_with_two_key_field_matches_marks_merge(self):
self.assertTrue(response.results[0].needs_merge)
self.assertEqual("exp-1", response.results[0].merge_candidate_id)

def test_sequential_merge_uses_first_non_duplicate_as_batch_candidate(self):
experiences = [
{
"experience_name": "캡스톤 프로젝트",
"experience_group": "상세 서술형",
"experience_type": "프로젝트",
"basic_info": {"project_name": "캡스톤 프로젝트"},
"experience_content": "추천 모델을 개발했습니다.",
},
{
"experience_name": "캡스톤 프로젝트",
"experience_group": "상세 서술형",
"experience_type": "프로젝트",
"basic_info": {"project_name": "캡스톤 프로젝트"},
"experience_content": "추천 모델을 개발했습니다.",
},
]

result = apply_sequential_merge_results_to_step2(
experiences,
[],
threshold=0.86,
embedding_client=SameOpenAI(),
)

self.assertFalse(result[0]["needs_merge"])
self.assertTrue(result[1]["needs_merge"])
self.assertEqual("batch:0", result[1]["merge_candidate_id"])


if __name__ == "__main__":
unittest.main()