- New agent/prompts.py: all LLM prompts in one place (edit to tune behaviour)
- config.py: add default_model, scraper_delay, recent_days, deadline_warn_days,
min_score_default, max_results_per_source
- app.py: remove model dropdown — hardcoded to config.default_model
- README: strip 4x duplicate HF frontmatter, update structure/model sections
- push_to_hf.sh: guard against prepending frontmatter if already present
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: filter stale jobs and add freshness indicator
- Filter out listings posted in previous years or with expired deadlines
- Add `freshness` field to each job: 🟢 Recent (≤30d), 🟡 Older,
🔴 Closing soon (deadline ≤14d), or "" when no date is available
- Show freshness in results table and job details panel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chore: remove DuckDuckGo references from app description
Only llama-3.3-70b-versatile and llama-3.1-8b-instant are currently
available on Groq's free production tier. Update app.py, README, and docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: remove DuckDuckGo scraper and 'any' position type; add docs link to README
- Remove WebSearchScraper from active scrapers in JobSearcher._build_scrapers
- Remove 'any' from position type dropdown, default to 'phd'
- Add documentation link to README
- Update all docs pages to reflect changes (sources, web-ui, overview, api, index)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: use public agent API in app.py; update README for new structure and Groq
- Add Groq (OpenAI-compatible) backend to LLMClient and config
- Auto-select Groq when GROQ_API_KEY secret is set, fall back to HF
- Update JobAgent to accept backend + api_key instead of HF-only token
- Raise LLMQuotaError (402) explicitly instead of silently scoring 0
- Switch model list to Groq models (llama-3.3-70b, llama-3.1-8b, gemma2, mixtral)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: make all scored positions reviewable, not just those above min_score
The minimum score now acts as a visual filter for the recommended table
only — all positions are available in the Review tab dropdown, sorted
by score. Users can review and approve any position regardless of score.
- job_matcher.score_all(): remove min_score filter, always return all
- pipeline.score_jobs(): drop min_score param (no longer needed)
- app.py: separate scored (all) from recommended (>= min_score);
scored_df shows recommended only, position_selector shows all
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
refactor: remove dead code, extract shared job helpers
agent/utils.py:
- Add job_institution(job) — eliminates repeated .get("institution", .get("company",...))
pattern; the "company" fallback was dead (no scraper ever sets it)
- Add job_description(job, max_chars) — eliminates repeated [:3000] truncation pattern
agent/job_searcher.py:
- Remove _TYPE_QUERY dict (defined but never referenced anywhere)
- Move _MLSCI_NON_COUNTRY from inside the loop body to module-level frozenset
- Fix module docstring (remove stale FindAPhD/jobs.ac.uk-only references)
agent/job_matcher.py, cover_letter.py, cv_tailor.py:
- Use job_institution() and job_description() from utils
app.py:
- Use job_institution() in all 7 display/logic locations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add "predoctoral" to position type dropdown in UI
- Add detection keywords: predoctoral, pre-doctoral, early-stage researcher,
research trainee, master student, research internship, ESR
- Add DuckDuckGo query label for predoctoral searches
- Add mlscientist category slug (maps to general jobs category)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docs: update README and app.py description to reflect current state
- README: list actual job sources (Euraxess, mlscientist, jobs.ac.uk, DDG),
add coverage table, update how-to-use steps, list available models
- app.py module docstring: update sources list, remove obsolete references
(FindAPhD, Academic Positions)
- app.py UI header: update to match actual sources and features
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: add mlscientist.com scraper; remove map from UI
- Add _search_mlscientist(): scrapes mlscientist.com via WordPress
category pages (/category/phd-positions/, /category/postdoc-positions/)
combined with keyword search (?s=); falls back to geographic category
slugs when location matches a known country
- Remove Leaflet map and _MAP_HTML from app.py; location input is now
a plain dropdown (allow_custom_value=True)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: location dropdown with interactive Leaflet map (Nominatim reverse geocoding)