Cover letter generation components
React pieces for drafting and editing cover letters.
Repository layout#
The cover letter generation feature spans frontend React components and backend FastAPI services. The frontend collects user inputs, manages state, and handles user interactions such as copying to clipboard and downloading. The backend processes the inputs, resolves job descriptions from URLs or text, personalizes content using LLM prompts, and returns formatted cover letters.
graph TB subgraph "Frontend" A["CoverLetterDetailsForm.tsx<br/>Collects user inputs"] B["GeneratedLetterPanel.tsx<br/>Displays and customizes generated letter"] C["page.tsx<br/>Main page orchestrating generation and editing"] D["cover-letter.service.ts<br/>API client for cover letter endpoints"] E["cover-letter.ts<br/>Type definitions"] end subgraph "Backend" F["routes/cover_letter.py<br/>FastAPI endpoints"] G["services/cover_letter.py<br/>LLM-driven generation and editing"] H["models/cover_letter/schemas.py<br/>Pydantic models"] I["agents/web_content_agent.py<br/>Fetches markdown from URLs"] end A --> C B --> C C --> D D --> F F --> G G --> I G --> H
Building blocks#
- CoverLetterDetailsForm: Collects personal details, job description (via URL or text), key points to highlight, additional context, and optional recipient/company information. Includes a mode toggle to switch between URL and text inputs for job descriptions.
- GeneratedLetterPanel: Displays the generated cover letter, supports editing mode with instruction input, copy-to-clipboard, and text download functionality.
- Backend Services: Handle generation and editing requests, resolve job descriptions, and apply LLM prompts to produce or refine cover letters.
Key responsibilities:
- Frontend: Manage form state, validate inputs, assemble FormData, and orchestrate API calls.
- Backend: Validate inputs, fetch external content when needed, execute LLM prompts, and return structured responses.
How it fits together#
The system follows a client-server architecture:
- Frontend collects inputs and sends them to backend endpoints.
- Backend resolves job descriptions, builds prompts, and invokes an LLM to generate or edit cover letters.
- Responses are returned as structured data with the cover letter content.
sequenceDiagram participant U as "User" participant F as "Frontend Page" participant S as "Service Client" participant R as "Routes" participant SVC as "Services" participant AG as "Web Content Agent" U->>F : "Fill form and click Generate" F->>S : "POST /api/cover-letter with FormData" S->>R : "Forward request" R->>SVC : "Call generate_cover_letter()" SVC->>AG : "Fetch markdown from JD URL (optional)" AG-->>SVC : "Markdown content" SVC-->>R : "Generated cover letter text" R-->>S : "CoverLetterResponse" S-->>F : "Update state with generated letter" F-->>U : "Display cover letter in panel"
CoverLetterDetailsForm#
Purpose:
- Collects all necessary inputs for cover letter generation, including personal details, job description (URL or text), key points to highlight, additional context, and optional recipient/company information.
Key features:
- Dual-input mode for job description: URL and text toggled via pill buttons.
- Controlled inputs bound to parent state via handleInputChange.
- Input sanitization and normalization (e.g., lowercasing sender name).
Processing logic:
- On mode switch, clears the inactive input to ensure only one source is submitted.
- Uses icons and animations for improved UX during mode transitions.
Validation and UX:
- Labels and placeholders guide users on required and optional fields.
- Animated transitions improve perceived responsiveness.
GeneratedLetterPanel#
Purpose:
- Displays the generated cover letter, supports editing mode, and provides actions for copying and downloading.
Key features:
- Edit mode toggle with instruction input area.
- Action buttons: edit, copy to clipboard, download as text.
- Markdown rendering for readable presentation.
Processing logic:
- Toggles edit mode visibility and controls button states.
- Applies disabled states during editing operations.
- Uses a Markdown renderer for content display.
Export and editing:
- Copy to clipboard uses browser API with user feedback.
- Download creates a Blob and triggers a temporary anchor download.
Frontend page orchestration (page.tsx)#
Purpose:
- Coordinates the entire cover letter generation flow, including resume selection, form submission, and editing.
Key responsibilities:
- Manages form state, resume selection modes (resume ID vs. file upload), and generated letter state.
- Validates inputs before sending requests.
- Handles API responses and user feedback via toasts.
- Implements copy-to-clipboard and download-as-text functionality.
Integration points:
- Uses ResumeSelector for choosing existing resumes or uploading new ones.
- Calls useGenerateCoverLetter and useEditCoverLetter hooks for mutations.
- Builds FormData dynamically based on selected resume mode and form inputs.
Backend routes (cover_letter.py)#
Purpose:
- Expose REST endpoints for generating and editing cover letters.
Endpoints:
- POST /cover-letter/generator/: Accepts resume text or file, job details, and preferences; returns generated cover letter.
- POST /cover-letter/edit/: Accepts the previous cover letter and edit instructions; returns updated cover letter.
Request handling:
- Extracts form fields and passes them to service functions.
- Builds resume data dictionary from raw text or file content.
- Delegates to LLM-based services for generation and editing.
Backend services (cover_letter.py)#
Purpose:
- Implement the core logic for cover letter generation and editing using LLM prompts.
Key functions:
- generate_cover_letter: Resolves job description (URL or text), constructs prompt, and invokes LLM.
- edit_cover_letter: Applies user instructions to refine an existing cover letter.
- _resolve_job_description: Fetches markdown from URL using web content agent and merges with manual text.
Prompts:
- COVER_LETTER_PROMPT: Defines structure, word limits, tone, and content requirements.
- COVER_LETTER_EDIT_PROMPT: Guides refinement based on explicit instructions.
External integration:
- Uses web_content_agent to fetch markdown from URLs.
- Converts language codes to readable names for prompt localization.
Data models (schemas.py)#
Purpose:
- Define request/response models for cover letter operations using Pydantic.
Models:
- CoverLetterRequest: Input fields for generation, including optional JD URL and additional info.
- CoverLetterEditRequest: Extends request model with generated cover letter and edit instructions.
- CoverLetterResponse: Standardized response containing success status, message, and cover letter body.
Validation:
- Enforces presence of required fields and defaults for optional ones.
- Ensures consistent shape for frontend-backend communication.
Frontend types (cover-letter.ts)#
Purpose:
- Define TypeScript interfaces for cover letter sessions, entries, requests, and response data.
Interfaces:
- CoverLetterSession and CoverLetterEntry: Represent stored cover letter history.
- CoverLetterRequest: Shape of data sent to backend, including optional fields for editing.
- CoverLetterResponseData: Response payload shape for generated letters.
Consistency:
- Mirrors backend models to ensure type-safe integration.
Web content agent (web_content_agent.py)#
Purpose:
- Fetch markdown content from external URLs using a third-party service.
Behavior:
- Sends GET request to a markdown extraction endpoint.
- Returns empty string on failure or invalid input.
- Used to resolve job descriptions from URLs.
How it fits together#
classDiagram
class CoverLetterDetailsForm {
+formData : object
+handleInputChange(field, value)
+switchMode(mode)
}
class GeneratedLetterPanel {
+generatedLetter : object
+editMode : boolean
+setEditMode(mode)
+editInstructions : string
+setEditInstructions(instructions)
+isEditing : boolean
+editCoverLetter()
+copyToClipboard(text)
+downloadAsText()
}
class CoverLetterPage {
+formData : object
+generatedLetter : object
+resumeFile : File
+selectedResumeId : string
+generateCoverLetter()
+editCoverLetter()
+copyToClipboard(text)
+downloadAsText()
}
class CoverLetterService {
+getCoverLetters()
+deleteCoverLetter(id)
+generateCoverLetter(formData)
+editCoverLetter(formData)
}
class CoverLetterRoutes {
+cover_letter_generate()
+cover_letter_edit()
}
class CoverLetterServices {
+generate_cover_letter()
+edit_cover_letter()
+_resolve_job_description()
}
class WebContentAgent {
+return_markdown(url)
}
CoverLetterDetailsForm --> CoverLetterPage : "updates state"
GeneratedLetterPanel --> CoverLetterPage : "controls UI"
CoverLetterPage --> CoverLetterService : "calls"
CoverLetterService --> CoverLetterRoutes : "HTTP requests"
CoverLetterRoutes --> CoverLetterServices : "delegates"
CoverLetterServices --> WebContentAgent : "fetches JD"Cover letter generation algorithm#
The generation algorithm follows a deterministic pipeline:
- Resolve job description:
- If a URL is provided, fetch markdown content via web content agent.
- Merge fetched content with manually entered text if both are present.
- Construct prompt:
- Use COVER_LETTER_PROMPT with resolved job description, resume data, recipient/company details, and user-specified highlights.
- Invoke LLM:
- Call LLM with a role-playing instruction to act as a professional career coach.
- Return result:
- Strip whitespace and return plain text cover letter.
flowchart TD Start(["Start"]) --> ResolveJD["Resolve Job Description<br/>- Check URL<br/>- Fetch markdown if present<br/>- Merge with manual text if provided"] ResolveJD --> BuildPrompt["Build Prompt Using COVER_LETTER_PROMPT"] BuildPrompt --> InvokeLLM["Invoke LLM with role instructions"] InvokeLLM --> PostProcess["Strip whitespace and normalize"] PostProcess --> End(["Return Cover Letter"])
Template system and personalization#
Template system:
- Two prompts are defined:
- COVER_LETTER_PROMPT: Controls structure, word limits, tone, and content requirements for new cover letters.
- COVER_LETTER_EDIT_PROMPT: Guides refinement of existing letters based on explicit instructions.
- Language localization:
- Output language is derived from language codes and injected into prompts.
Personalization features:
- Candidate details: sender name, desired role/goal.
- Recipient/company context: recipient name, company name.
- Resume integration: resume data passed as JSON to inform content.
- Additional context: key points to highlight and extra instructions for the LLM.
Formatting options:
- Plain text output enforced to avoid markdown or JSON in responses.
- Word limits and paragraph counts specified in prompts.
Integration with job descriptions and resume data#
Job description resolution:
- URL mode: Uses web content agent to fetch markdown from the provided URL.
- Text mode: Uses manually entered job description.
- Combined mode: Merges fetched content with manual text to preserve user-added context.
Resume data integration:
- Frontend sends either resumeId or file content.
- Backend constructs resume_data dictionary with raw_text for prompt injection.
Backend request models:
- Pydantic models validate and normalize inputs for generation and editing.
Editing capabilities#
Editing workflow:
- User enables edit mode and provides specific instructions.
- Frontend packages current resume data, job details, previous cover letter, and edit instructions.
- Backend applies COVER_LETTER_EDIT_PROMPT to refine the letter while preserving quality and constraints.
User controls:
- Edit instructions input with validation to prevent empty submissions.
- Disabled states during editing to prevent concurrent operations.
Export functionality#
Export options:
- Copy to clipboard: Uses browser clipboard API with user feedback.
- Download as text: Creates a Blob with text/plain type and triggers a temporary download link.
Frontend implementation:
- Clipboard: Handles errors gracefully and notifies the user.
- Download: Constructs filename and URL, removes DOM elements after download.
Dependencies#
The system splits work by layer:
- Frontend components depend on service clients and type definitions.
- Service clients depend on backend routes.
- Routes depend on service functions.
- Services depend on web content agent and Pydantic models.
Potential circular dependencies:
- None observed; dependencies flow unidirectionally from UI to backend.
External dependencies:
- Web content agent relies on third-party markdown extraction service.
- LLM invocation requires configured language model dependencies.
graph LR UI["CoverLetterDetailsForm.tsx"] --> Page["page.tsx"] UI --> Panel["GeneratedLetterPanel.tsx"] Page --> Service["cover-letter.service.ts"] Service --> Routes["routes/cover_letter.py"] Routes --> Services["services/cover_letter.py"] Services --> Agent["agents/web_content_agent.py"] Services --> Models["models/cover_letter/schemas.py"]
Performance#
- Job description fetching: Network latency for URL-based JDs can impact generation time. Consider caching or pre-fetching strategies.
- LLM invocation: Asynchronous calls are used; ensure proper timeout handling and retry logic if needed.
- Frontend rendering: Large cover letters are rendered efficiently using a dedicated Markdown renderer component.
- FormData assembly: Minimize unnecessary fields to reduce payload size and improve network performance.
Troubleshooting#
Common issues:
- Missing resume: Ensure a resume is selected or uploaded before generating.
- Empty sender name: Required field; provide candidate name to proceed.
- Invalid JD URL: Verify URL accessibility and correctness; fallback to text mode if needed.
- Edit without instructions: Provide explicit edit instructions to avoid submission errors.
- Copy/download failures: Browser permissions or unsupported features may cause failures; notify users accordingly.
User feedback:
- Toast notifications provide immediate feedback for success and error states.
- Disabled states prevent concurrent operations and improve reliability.