How the Transform Module Normalizes LLM JSON to JSON Resume Format

The transform module in interviewstreet/hiring-agent converts arbitrary LLM-generated JSON into the standardized JSON-Resume schema through a deterministic pipeline of section-specific transformers that normalize dates, flatten description arrays, and map ambiguous keys to canonical fields.

The hiring-agent repository provides a robust pipeline for processing resume data extracted by large language models. At its core, the transform module (transform.py) serves as the normalization engine that bridges raw LLM outputs with the structured JSON-Resume specification, ensuring downstream components receive validated, predictable data structures.

The Normalization Pipeline Architecture

The transformation process follows a sequential, deterministic flow where transform_parsed_data acts as the central dispatcher. This function receives the raw LLM output as a Python dictionary, validates that the top-level object is a mapping, and then routes each section to specialized transformer functions. The architecture ensures that even loosely structured LLM JSON—containing inconsistent field names, free-form date strings, or nested description arrays—emerges as a standards-compliant JSON-Resume document.

Core Transformation Functions

Entry Point and Validation

The transform_parsed_data function serves as the primary entry point in transform.py. It inspects the incoming dictionary for standard resume sections and delegates each to appropriate handlers. Before processing, it verifies that the input is a valid mapping, preventing malformed LLM outputs from propagating through the pipeline.

Normalizing Basics and Profiles

When a basics section exists, the module invokes transform_basics to standardize personal information and online profiles. This function processes profile URLs by extracting domain information and deriving canonical network names (GitHub, LinkedIn, etc.) using the helper functions extract_domain_from_url and get_network_name. If usernames are missing from profile URLs, the transformer extracts them automatically, ensuring the final JSON-Resume basics object contains properly structured profiles arrays with network, username, and url fields.

Standardizing Work Experience

The transform_work_experience function handles both work and work_experience keys, normalizing them into the JSON-Resume work schema. This transformer performs three critical operations:

  • Array Flattening: Converts description arrays (e.g., ["Built API", "Wrote tests"]) into single summary strings
  • Date Parsing: Processes free-form date ranges like "Jan-Mar 2021" into ISO-8601 startDate and endDate fields using parse_date_range
  • Key Mapping: Translates ambiguous LLM keys such as type or title into the canonical position field required by the specification

Handling Volunteer and Organizations

For non-professional experience, transform_organizations normalizes any organizations array into the JSON-Resume volunteer schema. This function provides default placeholders for dates and highlights when the LLM output lacks temporal information, ensuring schema compliance even with incomplete data.

Education Standardization

The transform_education function converts education entries by extracting degree information into studyType and area fields, as defined in transform.py. It parses GPA or percentage values into standardized strings and splits date ranges into separate startDate and endDate fields, handling variations in how LLMs represent academic timelines.

Formatting Awards and Achievements

Achievements are normalized through transform_achievements, which creates uniform award objects containing title, optional date, awarder, and summary fields. This ensures that recognition data from arbitrary LLM structures fits the JSON-Resume awards schema.

Categorizing Skills

The transform_skills_comprehensive function implements a dual-pathway approach for skills normalization. When the LLM returns a simple list of strings, the transformer categorizes them under "Programming Languages." For objects containing a category field, it preserves the existing structure. Additionally, the function merges top-level fields like librariesFrameworks, toolsPlatforms, and databases into descriptive category names, creating a comprehensive skills array that matches the JSON-Resume specification.

Processing Projects

Both standard projects and open-source projectsOpenSource sections are handled by transform_projects_comprehensive. This function extracts skill information from project titles using pipe-delimited formats (e.g., "MyApp | Python, Flask"), normalizes technologies fields, and produces uniform project entries with name, description, highlights, and keywords fields.

Preserving Meta and Miscellaneous Data

Any remaining top-level keys—including certificates, publications, languages, interests, references, and meta—are copied verbatim during final assembly in transform_parsed_data. This ensures the output dictionary complies with the full JSON-Resume schema without data loss.

Practical Implementation Examples

The following example demonstrates the complete normalization workflow from raw LLM output to validated JSON-Resume:

from transform import transform_parsed_data
from models import JSONResume

# `llm_output` is the raw JSON dict returned by the LLM

normalized = transform_parsed_data(llm_output)

# Build a proper JSONResume object (uses pydantic for validation)

resume = JSONResume(**normalized)

print(resume.json(indent=2))

For processing individual sections without the full pipeline:

from transform import transform_work_experience

raw_work = [
    {
        "title": "Software Engineer",
        "company": "Acme Corp",
        "startDate": "Jan-Mar 2021",
        "description": ["Built API", "Wrote tests"]
    }
]

standard_work = transform_work_experience(raw_work)
print(standard_work)

# → [{'name': '', 'position': 'Software Engineer', 'url': None,

#     'startDate': 'Jan 2021', 'endDate': 'Mar 2021',

#     'summary': 'Built API Wrote tests', 'highlights': []}]

Integration with the Hiring Agent Pipeline

The transform module operates between the LLM invocation layer and the validation/scoring layers. According to the interviewstreet/hiring-agent source code:

  • llm_utils.py handles the LLM invocation and initial JSON parsing before data reaches the transform module
  • models.py defines the JSONResume Pydantic model that validates the transformed output, ensuring type safety and schema compliance
  • score.py consumes the normalized JSONResume objects for downstream candidate evaluation, relying on the canonical structure produced by the transform functions

This architecture creates a robust pipeline that takes loosely-structured LLM JSON and guarantees a standards-compliant JSON-Resume document ready for analysis, scoring, or export.

Summary

  • The transform module serves as the normalization engine in interviewstreet/hiring-agent, converting raw LLM JSON into JSON-Resume format through transform_parsed_data and specialized section transformers.
  • Key functions include transform_basics for profile normalization, transform_work_experience for date parsing and key mapping, and transform_skills_comprehensive for category management.
  • Helper functions like parse_date_range, extract_domain_from_url, and get_network_name handle domain-specific normalization tasks.
  • The output is validated against the JSONResume Pydantic model in models.py, ensuring full compliance with the JSON-Resume specification before downstream processing.

Frequently Asked Questions

What is the JSON-Resume format and why does the hiring-agent use it?

JSON-Resume is an open-source standard for structuring resume data in JSON format. The hiring-agent uses this specification because it provides a consistent, machine-readable schema that enables reliable parsing, validation, and scoring across different candidate profiles, regardless of variations in the original LLM output structure.

How does the transform module handle inconsistent date formats from LLMs?

The transform_work_experience and transform_education functions utilize the parse_date_range helper to interpret free-form date strings (such as "Jan-Mar 2021" or "2020-Present") and convert them into standardized ISO-8601 date formats. This ensures chronological data remains comparable and validatable across all candidate entries.

What happens when the LLM uses ambiguous field names like "title" instead of "position"?

The transform_work_experience function implements explicit key mapping logic that translates ambiguous LLM field names—such as title, type, or role—into the canonical position field required by the JSON-Resume schema. This normalization occurs during the transformation process, ensuring standardization before validation.

How does the module ensure the transformed output is valid JSON-Resume?

After transformation, the normalized dictionary is passed to the JSONResume Pydantic model defined in models.py. This model enforces strict type checking and schema validation, ensuring that all required fields are present and correctly formatted according to the JSON-Resume specification before the data reaches the scoring layer in score.py.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →