Files
srbhr--resume-matcher/apps/backend/e2e_monitor/flow.py
T
wehub-resource-sync 5bdf4cc89a
Publish Docker Image / publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:36 +08:00

103 lines
3.4 KiB
Python

"""Flow moves (seed-master, tailor) + the pure scorer-runner.
The scorer-runner wraps the deterministic scorers already proven in
``tests/evals/scorers.py`` so the harness and the eval suite agree on what
"a good tailoring" means. (The HTTP moves are appended to this module in a
later task.)
"""
from __future__ import annotations
from pathlib import Path
from typing import Any
import httpx
from e2e_monitor import API_BASE
from tests.evals.scorers import (
is_valid_resume,
jd_keywords_present,
no_fabricated_employers,
personal_info_unchanged,
sections_preserved,
)
def score_tailoring(
original: dict[str, Any], tailored: dict[str, Any], keywords: list[str]
) -> dict[str, Any]:
"""Run every structural scorer over an (original, tailored) pair."""
return {
"sections_preserved": sections_preserved(original, tailored),
"fabricated_employers": no_fabricated_employers(original, tailored),
"personal_info_unchanged": personal_info_unchanged(original, tailored),
"is_valid_resume": is_valid_resume(tailored),
"jd_keyword_coverage": jd_keywords_present(tailored, keywords),
}
def seed_master_db(data_dir: Path, master: dict[str, Any]) -> str:
"""Pre-seed the isolated DB with a known master BEFORE the server boots.
The upload endpoint only accepts documents (and runs a non-deterministic LLM
parse), so for a controlled, deterministic master we write it straight into
the isolated TinyDB file via app.database.Database — the same file the server
opens once booted with DATA_DIR=<data_dir>. Returns the master's resume_id.
"""
from app.database import Database
db = Database(db_path=data_dir / "database.json")
try:
doc = db.create_resume(
content="(seeded master resume)",
content_type="md",
is_master=True,
processed_data=master,
processing_status="ready",
)
return doc["resume_id"]
finally:
db.close()
def tailor(
resume_id: str, jd_text: str, keywords: list[str], original: dict[str, Any]
) -> dict[str, Any]:
"""jobs/upload -> improve/preview -> improve/confirm; returns tailored + scores."""
jobs_resp = httpx.post(
f"{API_BASE}/jobs/upload",
json={"job_descriptions": [jd_text], "resume_id": resume_id},
timeout=120,
)
jobs_resp.raise_for_status()
job_ids = jobs_resp.json().get("job_id", [])
if not job_ids:
raise RuntimeError("jobs/upload returned no job_id")
job_id = job_ids[0]
preview_resp = httpx.post(
f"{API_BASE}/resumes/improve/preview",
json={"resume_id": resume_id, "job_id": job_id},
timeout=240,
)
preview_resp.raise_for_status()
data = preview_resp.json()["data"]
tailored = data["resume_preview"]
improvements = data["improvements"]
confirm_resp = httpx.post(
f"{API_BASE}/resumes/improve/confirm",
json={"resume_id": resume_id, "job_id": job_id,
"improved_data": tailored, "improvements": improvements},
timeout=240,
)
confirm_resp.raise_for_status()
confirm = confirm_resp.json()
return {
"job_id": job_id,
"tailored": tailored,
"tailored_resume_id": confirm["data"].get("resume_id"),
"keywords": keywords,
"scores": score_tailoring(original, tailored, keywords),
}