# red8-tech/jobscope architecture

> Architecture diagram of the GitHub repository red8-tech/jobscope, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/red8-tech/jobscope
- Repository: https://github.com/red8-tech/jobscope

## Overview

jobscope tracks job listings and matches resumes against them. Its main workflow ingests jobs from CSV or Remotive, normalizes CSV data, stores listings and skills in SQLite, then exposes typed REST endpoints for browsing and analytics. For matching, the API reads a saved resume and jobs, extracts resume skills through an LLM, retrieves text-similar jobs with TF-IDF, scores skill overlap and gaps, and persists results. The graph also shows skill backfill and startup seeding. Source excerpts establish most calls; some API-to-matcher wiring is visible only as configuration and documented behavior.

Main entry points: [`src/jobscope/api/app.py`](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/app.py) (FastAPI app), [`src/jobscope/api/routes.py`](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/routes.py) (REST routes), [`src/jobscope/db/repository.py`](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/db/repository.py) (Job repository), [`src/jobscope/ai/matching.py`](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/matching.py) (Resume matcher)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_ingest["Job ingestion"]
  node_ingest_routes["Ingest endpoint<br/>[routes.py]"]
  node_csv_source["CSV source<br/>[csv_source.py]"]
  node_cleaning["CSV cleaning<br/>[cleaning.py]"]
  node_remotive_source["Remotive source<br/>[remotive.py]"]
  node_job_model["Job model<br/>[models.py]"]
end

subgraph group_api_data["API and storage"]
  node_api["FastAPI app<br/>[app.py]"]
  node_api_routes["REST routes<br/>[routes.py]"]
  node_schemas["API schemas<br/>[schemas.py]"]
  node_repository["Job repository<br/>[repository.py]"]
  node_schema["SQL schema<br/>[schema.py]"]
  node_sqlite[("SQLite store")]
  node_resume["Saved resume"]
  node_match_results["Stored matches"]
end

subgraph group_ai["Resume matching"]
  node_extractor["Skill extractor<br/>[extractor.py]"]
  node_structured["Validated output<br/>[structured.py]"]
  node_llm_client["LLM client<br/>[llm.py]"]
  node_matcher["Resume matcher<br/>[matching.py]"]
  node_retriever["TF-IDF retriever<br/>[retrieval.py]"]
end

subgraph group_enrichment["Skill enrichment"]
  node_backfill["Skill backfill<br/>[backfill.py]"]
end

node_user(("API client"))
node_csv_file["CSV file"]
node_remotive_api{{"Remotive API"}}
node_groq{{"Groq LLM"}}

node_user -->|"sends requests"| node_api
node_api -->|"registers routes"| node_api_routes
node_api -->|"configures source"| node_csv_source
node_api -->|"configures source"| node_remotive_source
node_api -->|"configures factory"| node_matcher
node_api -->|"configures factory"| node_extractor
node_api -->|"seeds data"| node_repository
node_api_routes -->|"reads and writes"| node_repository
node_api_routes -->|"returns typed responses"| node_schemas
node_api_routes -->|"runs backfill"| node_backfill
node_api_routes -.->|"invokes matching"| node_matcher
node_ingest_routes -->|"dispatches source"| node_csv_source
node_ingest_routes -->|"dispatches source"| node_remotive_source
node_ingest_routes -->|"stores jobs"| node_repository
node_csv_file -->|"provides rows"| node_csv_source
node_csv_source -->|"cleans rows"| node_cleaning
node_csv_source -->|"creates jobs"| node_job_model
node_remotive_api -->|"provides listings"| node_remotive_source
node_remotive_source -->|"creates jobs"| node_job_model
node_repository -->|"queries and writes"| node_sqlite
node_repository -->|"initializes tables"| node_schema
node_repository -->|"maps records"| node_job_model
node_api_routes -->|"stores and reads"| node_resume
node_api_routes -->|"saves and reads"| node_match_results
node_matcher -->|"extracts skills"| node_extractor
node_matcher -->|"retrieves candidates"| node_retriever
node_matcher -->|"scores job listings"| node_job_model
node_groq -->|"answers prompts"| node_llm_client
node_extractor -->|"validates extraction"| node_structured
node_structured -->|"requests JSON"| node_llm_client
node_backfill -->|"loads and updates skills"| node_repository
node_backfill -->|"extracts job skills"| node_extractor

click node_ingest_routes "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/routes.py"
click node_csv_source "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ingestion/csv_source.py"
click node_cleaning "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ingestion/cleaning.py"
click node_remotive_source "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ingestion/remotive.py"
click node_job_model "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/models.py"
click node_api "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/app.py"
click node_api_routes "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/routes.py"
click node_schemas "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/schemas.py"
click node_repository "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/db/repository.py"
click node_schema "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/db/schema.py"
click node_backfill "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/services/backfill.py"
click node_extractor "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/extractor.py"
click node_structured "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/structured.py"
click node_llm_client "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/llm.py"
click node_matcher "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/matching.py"
click node_retriever "https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/retrieval.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_ingest_routes,node_csv_source,node_cleaning,node_remotive_source,node_job_model,node_user toneBlue
class node_api,node_api_routes,node_schemas,node_repository,node_schema,node_sqlite,node_resume,node_match_results toneAmber
class node_extractor,node_structured,node_llm_client,node_matcher,node_retriever,node_remotive_api toneMint
class node_backfill toneRose
class node_csv_file,node_groq toneIndigo
```

## Components

### Job ingestion

- [Ingest endpoint](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/routes.py): `src/jobscope/api/routes.py`
- [CSV source](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ingestion/csv_source.py): `src/jobscope/ingestion/csv_source.py`
- [CSV cleaning](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ingestion/cleaning.py): `src/jobscope/ingestion/cleaning.py`
- [Remotive source](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ingestion/remotive.py): `src/jobscope/ingestion/remotive.py`
- [Job model](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/models.py): `src/jobscope/models.py`

### API and storage

- [FastAPI app](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/app.py): `src/jobscope/api/app.py`
- [REST routes](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/routes.py): `src/jobscope/api/routes.py`
- [API schemas](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/api/schemas.py): `src/jobscope/api/schemas.py`
- [Job repository](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/db/repository.py): `src/jobscope/db/repository.py`
- [SQL schema](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/db/schema.py): `src/jobscope/db/schema.py`
- **SQLite store**
- **Saved resume**
- **Stored matches**

### Resume matching

- [Skill extractor](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/extractor.py): `src/jobscope/ai/extractor.py`
- [Validated output](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/structured.py): `src/jobscope/ai/structured.py`
- [LLM client](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/llm.py): `src/jobscope/ai/llm.py`
- [Resume matcher](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/matching.py): `src/jobscope/ai/matching.py`
- [TF-IDF retriever](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/ai/retrieval.py): `src/jobscope/ai/retrieval.py`

### Skill enrichment

- [Skill backfill](https://github.com/red8-tech/jobscope/blob/main/src/jobscope/services/backfill.py): `src/jobscope/services/backfill.py`

### Other components

- **API client**
- **CSV file**
- **Remotive API**
- **Groq LLM**

## Connections

- API client → FastAPI app: sends requests (evidence: `README.md`)
- FastAPI app → REST routes: registers routes (evidence: `src/jobscope/api/app.py`)
- FastAPI app → CSV source: configures source (evidence: `src/jobscope/api/app.py`)
- FastAPI app → Remotive source: configures source (evidence: `src/jobscope/api/app.py`)
- FastAPI app → Resume matcher: configures factory (evidence: `src/jobscope/api/app.py`)
- FastAPI app → Skill extractor: configures factory (evidence: `src/jobscope/api/app.py`)
- FastAPI app → Job repository: seeds data (evidence: `src/jobscope/api/app.py`)
- REST routes → Job repository: reads and writes (evidence: `src/jobscope/api/routes.py`)
- REST routes → API schemas: returns typed responses (evidence: `src/jobscope/api/routes.py`)
- REST routes → Skill backfill: runs backfill (evidence: `src/jobscope/api/routes.py`)
- REST routes → Resume matcher: invokes matching
- Ingest endpoint → CSV source: dispatches source (evidence: `src/jobscope/api/routes.py`)
- Ingest endpoint → Remotive source: dispatches source (evidence: `src/jobscope/api/routes.py`)
- Ingest endpoint → Job repository: stores jobs (evidence: `src/jobscope/api/routes.py`)
- CSV file → CSV source: provides rows (evidence: `README.md`)
- CSV source → CSV cleaning: cleans rows (evidence: `src/jobscope/ingestion/csv_source.py`)
- CSV source → Job model: creates jobs (evidence: `src/jobscope/ingestion/csv_source.py`)
- Remotive API → Remotive source: provides listings (evidence: `README.md`)
- Remotive source → Job model: creates jobs (evidence: `src/jobscope/ingestion/remotive.py`)
- Job repository → SQLite store: queries and writes (evidence: `src/jobscope/db/repository.py`)
- Job repository → SQL schema: initializes tables (evidence: `src/jobscope/db/repository.py`)
- Job repository → Job model: maps records (evidence: `src/jobscope/db/repository.py`)
- REST routes → Saved resume: stores and reads (evidence: `src/jobscope/api/routes.py`)
- REST routes → Stored matches: saves and reads (evidence: `src/jobscope/api/routes.py`)
- Resume matcher → Skill extractor: extracts skills (evidence: `src/jobscope/ai/matching.py`)
- Resume matcher → TF-IDF retriever: retrieves candidates (evidence: `src/jobscope/ai/matching.py`)
- Resume matcher → Job model: scores job listings (evidence: `src/jobscope/ai/matching.py`)
- Groq LLM → LLM client: answers prompts (evidence: `src/jobscope/ai/llm.py`)
- Skill extractor → Validated output: validates extraction (evidence: `src/jobscope/ai/extractor.py`)
- Validated output → LLM client: requests JSON (evidence: `src/jobscope/ai/structured.py`)
- Skill backfill → Job repository: loads and updates skills (evidence: `src/jobscope/services/backfill.py`)
- Skill backfill → Skill extractor: extracts job skills (evidence: `src/jobscope/services/backfill.py`)
