# How the Hiring Agent Caches Data for PDFs and GitHub Information

> Discover how the hiring agent caches PDF extraction and GitHub API data locally using JSON files to speed up subsequent evaluations. Learn more about optimizing your workflow.

- Repository: [HackerRank/hiring-agent](https://github.com/interviewstreet/hiring-agent)
- Tags: internals
- Published: 2026-07-19

---

**The hiring-agent tool persists expensive PDF extraction results and GitHub API responses to local JSON files within a `cache/` directory, but only when the `DEVELOPMENT_MODE` environment variable is truthy, allowing subsequent evaluations to skip redundant parsing and network requests.**

The interviewstreet/hiring-agent repository accelerates résumé scoring workflows by storing structured representations of processed data. When analyzing candidate profiles repeatedly during development, the tool avoids re-parsing PDF documents and re-fetching GitHub profiles by reading from validated cache files instead of performing expensive operations.

## Caching Architecture Overview

The hiring agent implements **conditional disk-based caching** for two specific high-cost operations:

1. **PDF extraction**: Converting résumé PDFs into structured `JSONResume` models
2. **GitHub data retrieval**: Fetching candidate profile information from the GitHub API

Both caches share a similar pattern but target different data sources. The cache logic resides primarily in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 14‑20, 26‑38, 57‑65, 69‑87, and 96‑104) with supporting utilities in [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/github.py). Caching activates exclusively when `DEVELOPMENT_MODE` evaluates to true, ensuring production runs always fetch fresh data.

## PDF Resume Caching

### Cache File Naming

When processing a PDF file, the hiring agent constructs a deterministic cache filename in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 14‑20) using the PDF's basename:

```python
cache_filename = f"cache/resumecache_{os.path.basename(pdf_path).replace('.pdf', '')}.json"

```

The `cache/` directory stores JSON files prefixed with `resumecache_` followed by the PDF filename without extension.

### Loading and Validation

During initialization, if `DEVELOPMENT_MODE` is enabled and the cache file exists, the system attempts to deserialize the cached data (lines 26‑38 in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py)):

```python
if DEVELOPMENT_MODE and os.path.exists(cache_filename):
    cached_data = json.loads(Path(cache_filename).read_text())
    if is_valid_resume_data(cached_data):
        resume_data = JSONResume(**cached_data)
    else:
        os.remove(cache_filename)

```

The helper function `is_valid_resume_data` validates core resume sections. If validation fails or JSON parsing raises an exception, the code removes the invalid cache file via `os.remove(cache_filename)` and proceeds with fresh PDF extraction.

### Writing Cache Entries

After successful PDF processing via `PDFHandler().extract_json_from_pdf(pdf_path)`, the tool writes the structured result to disk (lines 57‑65):

```python
if is_valid_resume_data(resume_data.model_dump()):
    Path(cache_filename).write_text(json.dumps(resume_data.model_dump(), indent=2))

```

This write operation only occurs when the extracted data contains valid core sections, preventing empty or malformed entries from polluting the cache.

## GitHub Data Caching

### Cache Structure

The GitHub caching mechanism follows a parallel structure but stores API response dictionaries rather than Pydantic models. In [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 69‑87), the system constructs a separate cache filename:

```python
github_cache_filename = f"cache/githubcache_{os.path.basename(pdf_path).replace('.pdf', '')}.json"

```

Note that despite caching GitHub profile data, the filename derives from the PDF basename, maintaining a 1:1 relationship between a résumé file and its associated cached GitHub information.

### Fetch and Store Logic

When `DEVELOPMENT_MODE` is active and the GitHub cache exists, the system loads the JSON and verifies it contains a required `"profile"` key:

```python
if DEVELOPMENT_MODE and os.path.exists(github_cache_filename):
    github_data = json.loads(Path(github_cache_filename).read_text())
    if "profile" not in github_data:
        os.remove(github_cache_filename)
        github_data = fetch_and_display_github_info(github_profile.url)
else:
    github_data = fetch_and_display_github_info(github_profile.url)

```

Following a successful API call, the results persist to disk (lines 96‑104):

```python
with open(github_cache_filename, 'w') as f:
    json.dump(github_data, f, indent=2)

```

The [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/github.py) file contains additional cache management utilities, including the `_create_cache_filename` helper function.

## Activating Development Mode

To enable caching during development, export the environment variable before executing the scoring script:

```bash
export DEVELOPMENT_MODE=1
python score.py candidate_resume.pdf

```

When this variable is unset or falsy, the tool bypasses all cache checks and performs full PDF extraction and GitHub API requests on every run.

## Error Handling and Cache Invalidation

Both caching implementations follow **fail-fast validation** patterns:

- **Automatic invalidation**: Corrupted or outdated cache files trigger immediate deletion via `os.remove()` rather than attempting repair
- **Schema validation**: PDF caches validate against `JSONResume` model requirements, while GitHub caches check for specific required keys like `"profile"`
- **Atomic writes**: Cache writes use `Path.write_text()` or `json.dump()` with explicit file handles to ensure complete data persistence

The cache directory must exist before running; the code assumes the `cache/` folder is available in the working directory.

## Summary

- The hiring-agent tool caches **PDF extractions** and **GitHub API responses** in separate JSON files within the `cache/` directory
- Caching only activates when `DEVELOPMENT_MODE` is truthy, ensuring production runs fetch fresh data
- **PDF cache files** follow the pattern `cache/resumecache_<pdf-name>.json` and store validated `JSONResume` models
- **GitHub cache files** follow `cache/githubcache_<pdf-name>.json` and store profile dictionaries requiring a `"profile"` key
- Invalid cache files are automatically removed via `os.remove()` when validation fails, triggering fresh data extraction
- Primary implementation resides in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 14‑104) with supporting logic in [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/github.py)

## Frequently Asked Questions

### Where does the hiring agent store cached PDF and GitHub data?

The tool stores cached data in the `cache/` directory within the project root. PDF extractions save as `cache/resumecache_<pdf-name>.json`, while GitHub profile data saves as `cache/githubcache_<pdf-name>.json`, where `<pdf-name>` derives from the original PDF filename without the `.pdf` extension.

### How do I enable caching in the hiring agent?

Set the `DEVELOPMENT_MODE` environment variable to a truthy value before running the script. For example: `export DEVELOPMENT_MODE=1`. When this variable is not set or evaluates to false, the tool ignores cache files and performs fresh PDF parsing and GitHub API requests.

### What happens if a cache file becomes corrupted?

If JSON deserialization fails or validation checks (like `is_valid_resume_data()` for PDFs or the `"profile"` key check for GitHub data) determine the cache is invalid, the code immediately removes the file using `os.remove()` and proceeds with the expensive operation to regenerate valid data.

### Why does the GitHub cache filename use the PDF name instead of the GitHub username?

The hiring agent associates GitHub data with specific résumé submissions rather than standalone GitHub identities. Using the PDF basename ensures that cached GitHub information remains linked to the particular candidate file being processed, preventing confusion when evaluating multiple candidates who might share similar GitHub profile data.