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
80 changes: 22 additions & 58 deletions dingo/model/llm/llm_keyword_matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,12 @@
from typing import List

from dingo.io import Data
from dingo.io.output.eval_detail import EvalDetail, QualityLabel
from dingo.model import Model
from dingo.model.llm.base_openai import BaseOpenAI
from dingo.utils import log
from dingo.utils.exception import ConvertJsonError

# Import EvalDetail for dev branch compatibility, fallback to ModelRes for main branch
try:
from dingo.io.output.eval_detail import EvalDetail, QualityLabel
USE_EVAL_DETAIL = True
except ImportError:
from dingo.model.modelres import ModelRes
USE_EVAL_DETAIL = False

# Complete synonym mapping for keyword normalization
SYNONYM_MAP = {
"k8s": "Kubernetes",
Expand Down Expand Up @@ -185,7 +178,7 @@ def _build_prompt(jd_text: str, resume_text: str) -> str:

@classmethod
def process_response(cls, response: str):
"""Process LLM response. Returns EvalDetail (dev) or ModelRes (main)."""
"""Process LLM response. Returns EvalDetail."""
log.info(f"Raw LLM response: {response}")

# Extract think content and clean response
Expand Down Expand Up @@ -213,29 +206,16 @@ def process_response(cls, response: str):

log.info(f"Keyword match score: {score:.1%}, threshold: {cls.threshold:.0%}")

# Return appropriate result type based on branch
if USE_EVAL_DETAIL:
result = EvalDetail(metric=cls.__name__)
result.score = score
result.reason = [reason]
if score >= cls.threshold:
result.status = False
result.label = [QualityLabel.QUALITY_GOOD]
else:
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]
# Return EvalDetail result
result = EvalDetail(metric=cls.__name__)
result.score = score
result.reason = [reason]
if score >= cls.threshold:
result.status = False
result.label = [QualityLabel.QUALITY_GOOD]
else:
result = ModelRes()
result.score = score
result.reason = [reason]
if score >= cls.threshold:
result.error_status = False
result.type = "KEYWORD_MATCH_GOOD"
result.name = "MATCH_GOOD"
else:
result.error_status = True
result.type = "KEYWORD_MATCH_LOW"
result.name = "MATCH_LOW"
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]

return result

Expand Down Expand Up @@ -346,38 +326,22 @@ def _generate_reason(cls, jd_analysis: dict, keyword_analysis: List[dict], score

@classmethod
def eval(cls, input_data: Data):
"""Override eval to validate inputs. Returns EvalDetail (dev) or ModelRes (main)."""
"""Override eval to validate inputs. Returns EvalDetail."""
# Validate that content (resume) is provided
if not input_data.content:
if USE_EVAL_DETAIL:
result = EvalDetail(metric=cls.__name__)
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]
result.reason = ["Resume text (content) is required but was not provided"]
return result
else:
return ModelRes(
error_status=True,
type="KEYWORD_MATCH_ERROR",
name="MISSING_RESUME",
reason=["Resume text (content) is required but was not provided"]
)
result = EvalDetail(metric=cls.__name__)
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]
result.reason = ["Resume text (content) is required but was not provided"]
return result

# Validate that prompt (JD) is provided
if not input_data.prompt:
if USE_EVAL_DETAIL:
result = EvalDetail(metric=cls.__name__)
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]
result.reason = ["Job description (prompt) is required but was not provided"]
return result
else:
return ModelRes(
error_status=True,
type="KEYWORD_MATCH_ERROR",
name="MISSING_JD",
reason=["Job description (prompt) is required but was not provided"]
)
result = EvalDetail(metric=cls.__name__)
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]
result.reason = ["Job description (prompt) is required but was not provided"]
return result

# Call parent eval method
return super().eval(input_data)
54 changes: 15 additions & 39 deletions dingo/model/llm/llm_resume_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,12 @@
from typing import List, Tuple

from dingo.io import Data
from dingo.io.output.eval_detail import EvalDetail, QualityLabel
from dingo.model import Model
from dingo.model.llm.base_openai import BaseOpenAI
from dingo.utils import log
from dingo.utils.exception import ConvertJsonError

# Import EvalDetail for dev branch compatibility, fallback to ModelRes for main branch
try:
from dingo.io.output.eval_detail import EvalDetail, QualityLabel
USE_EVAL_DETAIL = True
except ImportError:
from dingo.model.modelres import ModelRes
USE_EVAL_DETAIL = False


@Model.llm_register("LLMResumeOptimizer")
class LLMResumeOptimizer(BaseOpenAI):
Expand Down Expand Up @@ -191,7 +184,7 @@ def _parse_match_report(cls, match_report) -> Tuple[List[str], List[str], List[s

@classmethod
def process_response(cls, response: str):
"""Process LLM response. Returns EvalDetail (dev) or ModelRes (main)."""
"""Process LLM response. Returns EvalDetail."""
log.info(f"Raw LLM response length: {len(response)} chars")

# Clean response
Expand All @@ -210,22 +203,13 @@ def process_response(cls, response: str):
# Generate reason text
reason = cls._generate_reason(optimization_summary, section_changes, overall_improvement)

# Return appropriate result type based on branch
if USE_EVAL_DETAIL:
result = EvalDetail(metric=cls.__name__)
result.status = False
result.label = [QualityLabel.QUALITY_GOOD]
result.reason = [reason]
# Store full response for downstream use (using extra field)
result.optimized_content = response_json
else:
result = ModelRes()
result.error_status = False
result.type = "RESUME_OPTIMIZED"
result.name = "OPTIMIZATION_COMPLETE"
result.reason = [reason]
# Store full response for downstream use
result.optimized_content = response_json
# Return EvalDetail result
result = EvalDetail(metric=cls.__name__)
result.status = False
result.label = [QualityLabel.QUALITY_GOOD]
result.reason = [reason]
# Store full response for downstream use (using extra field)
result.optimized_content = response_json

return result

Expand Down Expand Up @@ -287,22 +271,14 @@ def _generate_reason(cls, summary: dict, changes: List[dict], overall: str) -> s

@classmethod
def eval(cls, input_data: Data):
"""Override eval to validate inputs. Returns EvalDetail (dev) or ModelRes (main)."""
"""Override eval to validate inputs. Returns EvalDetail."""
# Validate that content (resume) is provided
if not input_data.content:
if USE_EVAL_DETAIL:
result = EvalDetail(metric=cls.__name__)
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]
result.reason = ["Resume text (content) is required but was not provided"]
return result
else:
return ModelRes(
error_status=True,
type="RESUME_OPTIMIZER_ERROR",
name="MISSING_RESUME",
reason=["Resume text (content) is required but was not provided"]
)
result = EvalDetail(metric=cls.__name__)
result.status = True
result.label = [f"QUALITY_BAD.{cls.__name__}"]
result.reason = ["Resume text (content) is required but was not provided"]
return result

# Call parent eval method
return super().eval(input_data)
Expand Down