feat: add Jobgether recruiter framing to cover letter generation
Some checks failed
CI / test (push) Failing after 32s

When source == "jobgether", build_prompt() injects a recruiter context
note directing the LLM to address the Jobgether recruiter using
"Your client [at {company}] will appreciate..." framing rather than
addressing the employer directly. generate() and task_runner both
thread the is_jobgether flag through automatically.
This commit is contained in:
pyr0ball 2026-03-15 09:39:06 -07:00
parent e6257249cb
commit 94fd2ea332
4 changed files with 64 additions and 2 deletions

View file

@ -189,6 +189,7 @@ def build_prompt(
description: str, description: str,
examples: list[dict], examples: list[dict],
mission_hint: str | None = None, mission_hint: str | None = None,
is_jobgether: bool = False,
) -> str: ) -> str:
parts = [SYSTEM_CONTEXT.strip(), ""] parts = [SYSTEM_CONTEXT.strip(), ""]
if examples: if examples:
@ -202,6 +203,24 @@ def build_prompt(
if mission_hint: if mission_hint:
parts.append(f"⭐ Mission alignment note (for Para 3): {mission_hint}\n") parts.append(f"⭐ Mission alignment note (for Para 3): {mission_hint}\n")
if is_jobgether:
if company and company.lower() != "jobgether":
recruiter_note = (
f"🤝 Recruiter context: This listing is posted by Jobgether on behalf of "
f"{company}. Address the cover letter to the Jobgether recruiter, not directly "
f"to the hiring company. Use framing like 'Your client at {company} will "
f"appreciate...' rather than addressing {company} directly. The role "
f"requirements are those of the actual employer."
)
else:
recruiter_note = (
"🤝 Recruiter context: This listing is posted by Jobgether on behalf of an "
"undisclosed employer. Address the cover letter to the Jobgether recruiter. "
"Use framing like 'Your client will appreciate...' rather than addressing "
"the company directly."
)
parts.append(f"{recruiter_note}\n")
parts.append(f"Now write a new cover letter for:") parts.append(f"Now write a new cover letter for:")
parts.append(f" Role: {title}") parts.append(f" Role: {title}")
parts.append(f" Company: {company}") parts.append(f" Company: {company}")
@ -236,6 +255,7 @@ def generate(
description: str = "", description: str = "",
previous_result: str = "", previous_result: str = "",
feedback: str = "", feedback: str = "",
is_jobgether: bool = False,
_router=None, _router=None,
) -> str: ) -> str:
"""Generate a cover letter and return it as a string. """Generate a cover letter and return it as a string.
@ -251,7 +271,8 @@ def generate(
mission_hint = detect_mission_alignment(company, description) mission_hint = detect_mission_alignment(company, description)
if mission_hint: if mission_hint:
print(f"[cover-letter] Mission alignment detected for {company}", file=sys.stderr) print(f"[cover-letter] Mission alignment detected for {company}", file=sys.stderr)
prompt = build_prompt(title, company, description, examples, mission_hint=mission_hint) prompt = build_prompt(title, company, description, examples,
mission_hint=mission_hint, is_jobgether=is_jobgether)
if previous_result: if previous_result:
prompt += f"\n\n---\nPrevious draft:\n{previous_result}" prompt += f"\n\n---\nPrevious draft:\n{previous_result}"

View file

@ -169,6 +169,7 @@ def _run_task(db_path: Path, task_id: int, task_type: str, job_id: int,
job.get("description", ""), job.get("description", ""),
previous_result=p.get("previous_result", ""), previous_result=p.get("previous_result", ""),
feedback=p.get("feedback", ""), feedback=p.get("feedback", ""),
is_jobgether=job.get("source") == "jobgether",
) )
update_cover_letter(db_path, job_id, result) update_cover_letter(db_path, job_id, result)

View file

@ -115,3 +115,41 @@ def test_generate_calls_llm_router():
mock_router.complete.assert_called_once() mock_router.complete.assert_called_once()
assert "Alex Rivera" in result assert "Alex Rivera" in result
# ── Jobgether recruiter framing tests ─────────────────────────────────────────
def test_build_prompt_jobgether_framing_unknown_company():
from scripts.generate_cover_letter import build_prompt
prompt = build_prompt(
title="Customer Success Manager",
company="Jobgether",
description="CSM role at an undisclosed company.",
examples=[],
is_jobgether=True,
)
assert "Your client" in prompt
assert "jobgether" in prompt.lower()
def test_build_prompt_jobgether_framing_known_company():
from scripts.generate_cover_letter import build_prompt
prompt = build_prompt(
title="Customer Success Manager",
company="Resware",
description="CSM role at Resware.",
examples=[],
is_jobgether=True,
)
assert "Your client at Resware" in prompt
def test_build_prompt_no_jobgether_framing_by_default():
from scripts.generate_cover_letter import build_prompt
prompt = build_prompt(
title="Customer Success Manager",
company="Acme Corp",
description="CSM role.",
examples=[],
)
assert "Your client" not in prompt

View file

@ -79,10 +79,12 @@ class TestTaskRunnerCoverLetterParams:
"""Invoke _run_task for cover_letter and return captured generate() kwargs.""" """Invoke _run_task for cover_letter and return captured generate() kwargs."""
captured = {} captured = {}
def mock_generate(title, company, description="", previous_result="", feedback="", _router=None): def mock_generate(title, company, description="", previous_result="", feedback="",
is_jobgether=False, _router=None):
captured.update({ captured.update({
"title": title, "company": company, "title": title, "company": company,
"previous_result": previous_result, "feedback": feedback, "previous_result": previous_result, "feedback": feedback,
"is_jobgether": is_jobgether,
}) })
return "Generated letter" return "Generated letter"