Skip to main content

profgen (profile generator)

Project generated with PyScaffold

Convert candidate CVs into standardised Word profiles — without inventing facts.

profgen (the profile generator) turns a candidate CV (PDF/DOCX/TXT) into a standardised Word profile through a verbatim-extract → typed-structure → grounding-check → render → review-report pipeline. The profile is rendered against a template you supply, so any house style — including a private or corporate one — can be applied without the template living in the package.

The one hard rule: no invented facts

Omitting information is acceptable; fabricating a company, tool, date, qualification, institution or project is a defect. Concretely:

  • Anything absent from the source CV is marked "Not stated" (scalars) or left as an empty list — never guessed.
  • A deterministic, LLM-independent grounding check verifies that every extracted hard fact — company, tool, certification, institution, dates and project domain — actually appears in the source text. Anything it cannot find is flagged in the review report. Inferred or curated content (the summary, the derived grade, the applied discipline) is exempt from grounding and surfaced in the review report for human sign-off instead.
  • Employers are anonymised. Experience is rendered as Project N | <domain> rather than by company name (the company is still extracted, purely so the grounding check can confirm nothing was invented).
  • No fabricated hard facts. The renderer derives nothing itself: a total years-of-experience figure is never computed from dates and seniority titles are never invented. On the Claude path the skills table's per-skill "Years Experience" column may be estimated from the supporting employment span (balanced/bold only) — but every such estimate is recorded as an inferred value in the review report for sign-off; otherwise the column reads "Not stated".

Each conversion therefore writes two files: the .docx profile and a sibling *.review.md listing missing information and everything to verify before customer submission.

Installation

profgen is published on PyPI (Python 3.11+):

pip install profgen

Or install from source for development:

git clone https://github.com/ksteptoe/profgen
cd profgen
make dev                  # editable install with all dev/docs extras
# or, equivalently:
pip install -e ".[dev]"

Quickstart

# 1. Generate a starter .docx style-donor template (neutral default styles).
profgen make-template templates/profile_template.docx

# 2. Convert a CV offline (no API key, no network) — produces out.docx AND out.review.md.
profgen convert cv.pdf --output out.docx --offline

When --output is omitted the profile is written to <source-stem>_profile.docx in the current directory (so cv.pdf becomes cv_profile.docx), with the review report alongside.

cv-formatter is an identical alias for profgen, and python -m profgen works too. Run profgen convert --help for the full option list.

Bring your own template

The renderer binds content to ten logical rolestitle, section_heading, date_heading, project_method, subsection_label, body, bullet, domain_line, legal and table — rather than to fixed style names. By default each role maps to a neutral built-in or starter style (DEFAULT_STYLE_MAP), e.g. titleProfile Title, date_headingProfile Date, bodyNormal, bulletList Bullet, legalProfile Legal, tableTable Grid. The full role-to-style table is in the user guide.

To apply your own house style, pass your branded document with --template and a TOML style map with --style-map that points each role at the real paragraph style names in your document:

profgen convert cv.pdf --template my_template.docx --style-map my-style-map.toml
# my-style-map.toml — map the logical roles to YOUR template's style names.
title        = "My Heading Style"
date_heading = "My Date Style"
legal        = "My Legal Style"

The map may be partial: any role you omit falls back to its default (so a legacy map naming only the original five roles still loads). This is how a private or corporate template can be applied without it ever living in the package. The donor document's header, footer, fonts and logo media are preserved. The complete, ready-to-copy map ships at examples/style-map.example.toml.

One-step branded profile (make profile)

make profile renders a finished, branded profile from a candidate's CV using your private (gitignored) local/ assets. It is the one-command wrapper around profgen convert.

cp examples/style-map.example.toml local/style-map.toml   # required — then edit
# drop your branded template at local/template.docx        # required
make profile CV=cv.pdf                                     # Claude path (needs ANTHROPIC_API_KEY)
make profile CV=cv.pdf OFFLINE=1                           # deterministic, network-free path
make profile CV=cv.pdf OUT=out.docx CREATIVITY=bold DISCIPLINE="Verification"

What it does

  1. Checks prerequisites — fails early with a clear message if CV= is missing, or if local/template.docx / local/style-map.toml are absent.
  2. Runs the pipelineprofgen convert <CV> against your branded template and style map, writing the .docx profile and its sibling *.review.md.

It expands to:

profgen convert "$CV" \
  --template local/template.docx --style-map local/style-map.toml \
  --creativity balanced \
  [--grade-map local/grade-map.toml]          # auto-added when the file exists
  [--discipline-config local/discipline.toml] # auto-added when the file exists
  [--job-spec local/job-spec.txt]             # auto-added when the file exists
  [--discipline "..."] [--output ...] [--offline]

Inputs (all under the gitignored local/):

Input Role Required?
local/template.docx your branded style-donor (header/footer/logo + named styles) yes
local/style-map.toml maps logical roles → your template's real style names (copy examples/style-map.example.toml) yes
local/grade-map.toml years-of-experience → grade ladder (copy examples/grade-map.example.toml) optional, auto-wired
local/discipline.toml per-discipline priority skills + tool groups (copy examples/discipline.example.toml) optional, auto-wired
local/job-spec.txt per-run brief that augments the discipline defaults optional, auto-wired

The optional three are added only when the file exists, so a minimal setup with just the template and style map works.

Variables

  • CV= — the source CV (.pdf / .docx / .txt). Required.
  • OUT= — output path (default <source-stem>_profile.docx in the current directory).
  • OFFLINE=1 — use the deterministic, network-free heuristic instead of Claude.
  • CREATIVITY=strict / balanced (default) / bold; gates only the inferred fields.
  • DISCIPLINE= — overrides the inferred discipline.

Default vs offline

  • Default — the Claude path; needs ANTHROPIC_API_KEY. Produces the curated, client-grade profile.
  • OFFLINE=1 — no network, fully deterministic; a degraded skeleton (grounded facts only, no curation) that ignores CREATIVITY, DISCIPLINE and the job-spec. Useful for a quick structural check without an API key.

The local/ directory and .env are gitignored, so confidential templates, configs and API keys stay out of the repository. Override the asset directory with LOCAL_DIR= (e.g. make profile CV=cv.pdf LOCAL_DIR=clientA) to keep per-client asset sets.

Targeting and identity

Two further TOML files let you describe your internal grade ladder and the disciplines you staff. Starter files ship under examples/; copy each to the gitignored local/ and edit it, then pass it on the command line:

cp examples/grade-map.example.toml   local/grade-map.toml
cp examples/discipline.example.toml  local/discipline.toml
profgen convert cv.pdf --grade-map local/grade-map.toml \
    --discipline-config local/discipline.toml --discipline Verification
  • grade-map.toml — your years-of-experience → grade-title ladder.
  • discipline.toml — per-discipline priority skills and tool groupings.

All of --grade-map, --discipline, --discipline-config, --id-mode, --id, --creativity and --job-spec are active. The grade is derived from the candidate's stated years (and stays "Not stated" when no years are given), the operator's discipline is applied, and the candidate's identity is abstracted — --id-mode initials renders "Valentin Vasilescu" as "VV", --id-mode number gives a stable number, --id forces an explicit value, and --id-mode none keeps the real name. The real name is always retained for grounding but only shown with --id-mode none.

On the Claude path, --creativity {strict,balanced,bold} (default balanced) tunes how much interpretive licence the structuring step takes over inferred fields — hard facts stay strictly grounded at every level — and --job-spec FILE plus a matched --discipline steer skill prioritisation and the summary toward that role. The --offline heuristic client ignores these (it curates nothing). See the user guide for the full details.

Offline vs real Claude path

The structuring stage has two interchangeable backends behind one interface:

  • Offline (--offline) — the deterministic, network-free HeuristicStructuringClient. Needs no API key, makes no network call, and is what the entire test suite uses. Ideal for plumbing checks and CI.
  • Real Claude (default) — the ClaudeStructuringClient, which calls the Anthropic API and needs ANTHROPIC_API_KEY. This path is deliberately never exercised in CI; it is smoke-tested only behind an explicit opt-in (see examples/smoke_real_path.py).

Example

A runnable, fully-offline example builds a profile from a bundled synthetic CV with no API key:

.venv/bin/python examples/build_example_profile.py

It reads examples/input_cvs/sample_cv.txt, runs the offline pipeline, and writes the profile and its review report into examples/output_profiles/ (gitignored).

Development

make dev      # editable install with all dependencies
make test     # run the fully-offline test suite
make lint     # ruff
make format   # ruff --fix
make docs     # build the Sphinx HTML User Guide
make docs-pdf # build a single PDF of the docs (needs a LaTeX toolchain)

Quality gates: ruff clean, mypy --strict clean (scoped to src/), and pytest green with the network disabled. The Sphinx User Guide (make docs, or docs/userguide.md) is the full pipeline walkthrough and the shipped reference for every option and configuration file.

make docs-pdf produces docs/_build/latex/profgen.pdf. It needs a system LaTeX toolchain on PATHxelatex, latexmk, and makeindex (install TeX Live or, on Windows, MiKTeX). The toolchain is not pip-installable and is optional: the target fails fast with a clear message if latexmk is missing.

Note

This project has been set up using PyScaffold 4.6 with the ClickStart extension.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

profgen-0.0.2.tar.gz (131.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

profgen-0.0.2-py3-none-any.whl (48.7 kB view details)

Uploaded Python 3

File details

Details for the file profgen-0.0.2.tar.gz.

File metadata

  • Download URL: profgen-0.0.2.tar.gz
  • Upload date:
  • Size: 131.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for profgen-0.0.2.tar.gz
Algorithm Hash digest
SHA256 c4678807ae99a02da6d4eeeca4e85ab7727e5d1393c1a27b84fce42fc4b12976
MD5 73256b7d4df71118609217063d34a350
BLAKE2b-256 bd7dd2a296eebbf5e1042e2b20794f0bd47f633606c6eecbe5bafa9a01dc4351

See more details on using hashes here.

Provenance

The following attestation bundles were made for profgen-0.0.2.tar.gz:

Publisher: ci.yml on ksteptoe/profgen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file profgen-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: profgen-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 48.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for profgen-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 4f9c08b26fb1466a187a02a5088be1b2d6864db98d89f89ab90b6320044fd244
MD5 94e16e69087ab9c4404a80a556d47cc3
BLAKE2b-256 6d9053c38157bec895c8b79de1c6357f066444c8fcc4a78570f635292100c177

See more details on using hashes here.

Provenance

The following attestation bundles were made for profgen-0.0.2-py3-none-any.whl:

Publisher: ci.yml on ksteptoe/profgen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page