# Resume Agent

A local, evidence-backed agent that learns a canonical version of your career history
once, then tailors your resume to a job URL or pasted job post without inventing facts.

## How it works

1. **Profile:** reads your master resume and optional career notes, then extracts a
   structured profile. Every usable claim receives an evidence ID and source excerpt.
2. **Tailor:** analyzes the job, selects and rewrites only supported facts, and produces
   an ATS-friendly Markdown resume.
3. **Audit:** makes a separate LLM pass and runs deterministic checks to reject unknown
   evidence IDs, unsupported claims, and changed contact information.

The profile is your source of truth. Tailoring may reorder, shorten, or rephrase facts;
it must not create experience you do not have.

## Setup

You need Python 3.11+ and access to either OpenAI or an OpenAI-compatible API.

```bash
uv sync --extra dev
cp .env.example .env
```

The app loads `.env` automatically. The default OpenAI model is `gpt-5.6-terra`;
override it with `RESUME_AGENT_MODEL` or `--model`.

For an OpenAI-compatible provider, configure the endpoint without putting credentials
in the repository:

```dotenv
RESUME_AGENT_API_KEY=your-rotated-token
RESUME_AGENT_BASE_URL=https://service.nokod.ir/v1
RESUME_AGENT_API_STYLE=chat
RESUME_AGENT_LLM_TIMEOUT_SECONDS=1800
```

Discover the provider's exact model IDs, then add your choice to `.env`:

```bash
uv run resume-agent models
```

```dotenv
RESUME_AGENT_MODEL=the-model-id-you-selected
```
Custom endpoints default to the Chat Completions API because it is more widely
implemented by OpenAI-compatible services. Set `RESUME_AGENT_API_STYLE=responses` only
when the provider supports the Responses API and structured outputs.

Model calls are logged to stdout with a query ID, endpoint, model, API style, schema,
timing, prompt, input, parsed response, and full exception traceback. API keys are never
logged. Because prompts contain resume data, disable payload and response bodies while
keeping request metadata and errors with:

```dotenv
RESUME_AGENT_LOG_MODEL_PAYLOADS=false
```

`RESUME_AGENT_LLM_TIMEOUT_SECONDS` controls the provider read/write timeout. It defaults
to 1800 seconds (30 minutes), while connection establishment is capped at 30 seconds.
An upstream gateway may still enforce its own shorter timeout.

## Usage

### Web interface

Launch the local frontend:

```bash
uv run resume-agent editor-build
uv run resume-agent serve
```

Then open `http://127.0.0.1:8000`. Open `/cv/` (or click **Open CV editor**) for the
bundled Oh My CV Markdown editor. Its **AI Tailor with Signal** tool can learn the
current editor buffer, tailor it to a job URL or pasted description, apply the audited
Markdown directly, and restore the pre-AI version with one click.

### Docker Compose

After configuring `.env`, build and start the complete app with:

```bash
docker compose up --build
```

Open `http://127.0.0.1:8000`. The image builds Oh My CV and serves it from `/cv/` through
the same FastAPI process. Career-profile state and generated resumes persist in the
host's `.resume-agent/` and `output/` directories.

To use another host port, set `RESUME_AGENT_PORT` when starting Compose:

```bash
RESUME_AGENT_PORT=8080 docker compose up --build
```

Stop the service with `docker compose down`. This leaves the persisted profile and
outputs intact.

Both frontends include a **Tailoring strength** slider. At `0`, the agent stays close
to the source wording and organization. At `100`, it uses the fullest supported detail
and strongest truthful job alignment. The slider never relaxes evidence validation;
unsupported requirements remain gaps or follow-up questions rather than resume claims.

Below the slider, **Tailoring configuration** can switch between:

- **Strict source evidence:** every candidate-facing claim must map to one or more
  evidence IDs extracted from the source resume.
- **Flexible profile-based rewrite:** the complete profile and original resume remain
  the factual source, but claim-level evidence IDs are optional. This mode still tells
  the model not to invent employers, dates, skills, metrics, or achievements.

Use the **Evidence guard** control in Signal's header or the Oh My CV AI dialog to
disable claim-level evidence enforcement across the entire app. The setting is persisted
in `.resume-agent/settings.json` and applies to tailoring and revision chat. Turning it
off forces flexible profile mode everywhere; the supplied-profile truth and no-invention
rules remain active.

After tailoring in the main Signal interface, use **Open in Oh My CV** to create a new
local Oh My CV resume from the optimized Markdown and open it directly in the editor.

The tailored-results section also includes a revision chatbot. It can change tone,
emphasis, detail, ordering, and length, then refreshes the preview, match assessment,
honest gaps, downloads, and Oh My CV handoff. Every revision runs through the same
profile-evidence validation and factuality audit as the initial tailoring pass.

Long-running frontend actions use background tasks rather than holding one HTTP request
open. Profile extraction, tailoring, Oh My CV tailoring, and revision chat return a task
ID immediately; both frontends poll `/api/tasks/{task_id}` until the model work succeeds
or fails. Temporary polling connection errors are retried without cancelling model work.

The main browser interface supports resume upload,
career-profile review, job URLs or pasted descriptions, evidence-linked resume preview,
match analysis, and Markdown/JSON downloads. API credentials stay in the server
environment and are never returned to the browser.

Oh My CV is vendored from
[`Renovamen/oh-my-cv`](https://github.com/Renovamen/oh-my-cv) at the commit recorded in
`vendor/README.md`. It is GPL-3.0 licensed; preserve its license and source availability
if you distribute the combined application.

### Command line

Build your reusable profile:

```bash
uv run resume-agent profile build ./my-master-resume.pdf
```

Add factual context not captured in the resume:

```bash
uv run resume-agent profile build ./resume.docx \
  --about-file ./career-notes.md
```

Inspect what the agent believes about you:

```bash
uv run resume-agent profile show
```

Tailor from a URL:

```bash
uv run resume-agent tailor "https://company.example/jobs/123"
```

Or from a saved/pasted job post:

```bash
uv run resume-agent tailor ./job-post.txt
uv run resume-agent tailor "Paste the complete job description here..."
```

Outputs are written to `output/`:

- `resume.md`: clean tailored resume
- `resume-audited.md`: internal audit copy with evidence IDs in invisible HTML comments
- `report.md`: matches, real gaps, changes, and questions
- `tailoring.json`: complete structured result

Candidate-facing Markdown and Oh My CV PDF output never display evidence references such
as `[F013]`; those IDs remain available only in internal audit data.

## Privacy and safety

- Resume and profile files remain local, but their text is sent to the configured
  OpenAI API when you run `profile build` or `tailor`.
- Job URLs are restricted to public HTTP(S) addresses and response size is capped.
- Web-page instructions are treated as untrusted text, not agent instructions.
- Keep `.resume-agent/`, `.env`, and generated outputs out of version control.

## Development

```bash
uv run --extra dev pytest
uv run --extra dev ruff check .
```
