Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@
## 2025-02-18 - Regex Pre-compilation in Hot Paths
**Learning:** Re-compiling regexes inside a frequently called function (like `latex_escape` which runs for every string) creates significant overhead. Pre-compiling them at module level yielded a ~3.2x speedup.
**Action:** Always look for regex compilations inside loops or frequently called functions and move them to module level constants.

## 2025-02-18 - API Event Loop Starvation
**Learning:** In ASGI applications like FastAPI, running heavy synchronous tasks (like PDF compilation via `pdflatex`, running large ATS regex generation, or making blocking external AI calls) blocks the main thread, leading to event loop starvation and reducing throughput dramatically.
**Action:** Always wrap heavy synchronous functions using `functools.partial` and offload them to background worker threads using `await anyio.to_thread.run_sync()`.
31 changes: 25 additions & 6 deletions api/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import functools
import logging
import os
import tempfile
from pathlib import Path

import anyio
import yaml
from fastapi import FastAPI, HTTPException, Response, Security
from fastapi.middleware.cors import CORSMiddleware
Expand Down Expand Up @@ -171,7 +173,13 @@ async def render_pdf(request: ResumeRequest):
# We output to a temp file
output_pdf = temp_path / "output.pdf"

generator.generate(variant=request.variant, output_format="pdf", output_path=output_pdf)
func = functools.partial(
generator.generate,
variant=request.variant,
output_format="pdf",
output_path=output_pdf,
)
await anyio.to_thread.run_sync(func)

if not output_pdf.exists():
raise HTTPException(status_code=500, detail="PDF generation failed")
Expand Down Expand Up @@ -215,9 +223,12 @@ async def tailor_resume(request: TailorRequest):
# We pass None for yaml_path as we use direct data
generator = AIGenerator(yaml_path=None, config=config)

tailored_data = generator.tailor_data(
resume_data=request.resume_data, job_description=request.job_description
func = functools.partial(
generator.tailor_data,
resume_data=request.resume_data,
job_description=request.job_description,
)
tailored_data = await anyio.to_thread.run_sync(func)

return tailored_data

Expand Down Expand Up @@ -248,7 +259,10 @@ async def ats_check(request: ATSRequest):

# Generate ATS report directly from resume data
generator = ATSGenerator(config=config, resume_data=request.resume_data)
report = generator.generate_report(request.job_description, request.variant)
func = functools.partial(
generator.generate_report, request.job_description, request.variant
)
report = await anyio.to_thread.run_sync(func)

# Convert to JSON-serializable format
result = {
Expand Down Expand Up @@ -300,12 +314,14 @@ async def generate_cover_letter(request: CoverLetterRequest):

# Generate cover letter (always use non-interactive for API)
# The provided answers will be used as context by the AI
outputs, job_details = generator.generate_non_interactive(
func = functools.partial(
generator.generate_non_interactive,
job_description=request.job_description,
company_name=request.company_name,
variant=request.variant,
output_formats=[request.format],
)
outputs, job_details = await anyio.to_thread.run_sync(func)

# Return the generated content
# Note: outputs["md"] contains the rendered markdown, outputs["pdf"] contains LaTeX
Expand Down Expand Up @@ -560,7 +576,10 @@ async def render_resume_pdf(resume_id: str, variant: str = "base"):
generator = TemplateGenerator(yaml_path=resume_yaml_path)
output_pdf = temp_path / "output.pdf"

generator.generate(variant=variant, output_format="pdf", output_path=output_pdf)
func = functools.partial(
generator.generate, variant=variant, output_format="pdf", output_path=output_pdf
)
await anyio.to_thread.run_sync(func)

if not output_pdf.exists():
raise HTTPException(status_code=500, detail="PDF generation failed")
Expand Down
Loading