Commit graph

227 commits

Author SHA1 Message Date
da43578806 fix: get_config_dir had one extra .parent, resolved to /config not /app/config
All checks were successful
CI / test (push) Successful in 43s
2026-03-15 17:14:48 -07:00
5cb66d0bf1 Merge pull request 'feat: LLM queue optimizer — resource-aware batch scheduler (closes #2)' (#15) from feature/llm-queue-optimizer into main
All checks were successful
CI / test (push) Successful in 44s
2026-03-15 16:48:37 -07:00
971336841f fix: _trim_to_letter_end matches full name when no profile set
All checks were successful
CI / test (pull_request) Successful in 42s
When _profile is None the fallback pattern \w+ only matched the first
word of a two-word sign-off (e.g. 'Alex' from 'Alex Rivera'), silently
dropping the last name. Switch fallback to \w+(?:\s+\w+)? so a full
first+last sign-off is preserved in no-config environments (CI, first run).
2026-03-15 16:43:27 -07:00
4a996c2628 ci: apt-get update before installing libsqlcipher-dev
Some checks failed
CI / test (pull_request) Failing after 1m2s
2026-03-15 16:37:46 -07:00
5939ae88eb ci: install libsqlcipher-dev before pip install
Some checks failed
CI / test (pull_request) Failing after 7s
2026-03-15 16:36:50 -07:00
2bf73bbd44 ci: re-trigger after actions enabled
Some checks failed
CI / test (pull_request) Failing after 12s
2026-03-15 15:54:27 -07:00
72c1d4a945 ci: enable forgejo actions 2026-03-15 15:48:35 -07:00
782936bae4 ci: trigger runner 2026-03-15 15:39:45 -07:00
377fde239e docs: add Jobgether non-headless Playwright scraper to backlog
Some checks failed
CI / test (push) Failing after 12s
Xvfb-based Playwright can bypass Cloudflare Turnstile on jobgether.com.
Live inspection confirmed selectors; deferred pending Xvfb integration.
2026-03-15 11:59:48 -07:00
94fd2ea332 feat: add Jobgether recruiter framing to cover letter generation
Some checks failed
CI / test (push) Failing after 32s
When source == "jobgether", build_prompt() injects a recruiter context
note directing the LLM to address the Jobgether recruiter using
"Your client [at {company}] will appreciate..." framing rather than
addressing the employer directly. generate() and task_runner both
thread the is_jobgether flag through automatically.
2026-03-15 09:45:51 -07:00
e6257249cb feat: add Jobgether URL detection and scraper to scrape_url.py 2026-03-15 09:45:50 -07:00
4028667c62 feat: filter Jobgether listings via blocklist 2026-03-15 09:45:50 -07:00
a005397d5d docs: update spec — Jobgether discovery scraper not viable (Cloudflare + robots.txt) 2026-03-15 09:45:50 -07:00
17f7baae3c docs: add Jobgether integration implementation plan 2026-03-15 09:45:50 -07:00
ef9cb29518 docs: add cover letter recruiter framing to Jobgether spec 2026-03-15 09:45:50 -07:00
fe2f27c2a2 docs: add Jobgether integration design spec 2026-03-15 09:45:50 -07:00
e6f0e41de4 Merge pull request 'feat: LLM queue optimizer — resource-aware batch scheduler (closes #2)' (#13) from feature/llm-queue-optimizer into main
Some checks are pending
CI / test (push) Waiting to run
Reviewed-on: #13
2026-03-15 05:11:29 -07:00
22091760bd feat: LLM queue optimizer complete — closes #2
Some checks failed
CI / test (pull_request) Failing after 32s
Resource-aware batch scheduler for LLM tasks:
- scripts/task_scheduler.py (new): TaskScheduler singleton with VRAM-aware
  batch scheduling, durability, thread-safe singleton, memory safety
- scripts/task_runner.py: submit_task() routes LLM types through scheduler
- scripts/db.py: reset_running_tasks() for durable restart behavior
- app/app.py: _startup() preserves queued tasks on restart
- config/llm.yaml.example: scheduler VRAM budget config documented
- tests/test_task_scheduler.py (new): 24 tests covering all behaviors

Pre-existing failure: test_generate_calls_llm_router (issue #12, unrelated)
2026-03-15 05:01:24 -07:00
a17ba1e8d8 feat(app): use reset_running_tasks() on startup to preserve queued tasks 2026-03-15 04:57:49 -07:00
1139cd55ec feat(task_runner): route LLM tasks through scheduler in submit_task()
Replaces the spawn-per-task model for LLM task types with scheduler
routing: cover_letter, company_research, and wizard_generate are now
enqueued via the TaskScheduler singleton for VRAM-aware batching.
Non-LLM tasks (discovery, email_sync, etc.) continue to spawn daemon
threads directly. Adds autouse clean_scheduler fixture to
test_task_runner.py to prevent singleton cross-test contamination.
2026-03-15 04:52:42 -07:00
dfd2f0214e feat(scheduler): add durability — re-queue surviving LLM tasks on startup 2026-03-15 04:24:11 -07:00
1d9020c99a feat(scheduler): implement thread-safe singleton get_scheduler/reset_scheduler 2026-03-15 04:19:23 -07:00
84ce68af46 feat(scheduler): implement scheduler loop and batch worker with VRAM-aware scheduling 2026-03-15 04:14:56 -07:00
605e820fa6 feat(scheduler): implement enqueue() with depth guard and ghost-row cleanup 2026-03-15 04:05:22 -07:00
fa780af2f1 refactor(scheduler): use module-level _get_gpus directly in __init__ 2026-03-15 04:01:01 -07:00
cceacdd371 feat(scheduler): implement TaskScheduler.__init__ with budget loading and VRAM detection 2026-03-15 03:32:11 -07:00
0fedf7989e feat(scheduler): add task_scheduler.py skeleton with constants and TaskSpec 2026-03-15 03:28:43 -07:00
b664240340 docs(config): add scheduler VRAM budget config to llm.yaml.example 2026-03-15 03:28:26 -07:00
1f2273f049 refactor(tests): remove unused imports from test_task_scheduler 2026-03-15 03:27:17 -07:00
5ba654e414 feat(db): add reset_running_tasks() for durable scheduler restart 2026-03-15 03:22:45 -07:00
07166325dd docs: add LLM queue optimizer implementation plan
11-task TDD plan across 3 reviewed chunks. Covers:
- reset_running_tasks() db helper
- TaskScheduler skeleton + __init__ + enqueue + loop + workers
- Thread-safe singleton, durability, submit_task routing shim
- app.py startup change + full suite verification
2026-03-14 17:11:49 -07:00
7983f3365d docs: revise queue optimizer spec after review
Addresses 16 review findings across two passes:
- Clarify _active.pop/double-decrement non-issue
- Fix app.py change target (inline SQL, not kill_stuck_tasks)
- Scope durability to LLM types only
- Add _budgets to state table with load logic
- Fix singleton safety explanation (lock, not GIL)
- Ghost row fix: mark dropped tasks failed in DB
- Document static _available_vram as known limitation
- Fix test_llm_tasks_batch_by_type description
- Eliminate circular import via routing split in submit_task()
- Add missing budget warning at construction
2026-03-14 16:46:38 -07:00
9fcfe7daa1 docs: add LLM queue optimizer design spec
Resource-aware batch scheduler for LLM tasks. Closes #2.
2026-03-14 16:38:47 -07:00
397b778217 chore: add .worktrees/ to .gitignore
Prevents worktree directories from being tracked.
2026-03-14 16:30:38 -07:00
f403af4a31 fix(linkedin): update selectors for 2025 public DOM; surface login-wall limitation in UI
Some checks failed
CI / test (push) Has been cancelled
LinkedIn's unauthenticated public profile only exposes name, summary (truncated),
current employer name, and certifications. Past roles, education, and skills are
blurred server-side behind a login wall — not a scraper limitation.

- Update selectors: data-section='summary' (was 'about'), .profile-section-card
  for certs, .visible-list for current experience entry
- Strip login-wall noise injected into summary text after 'see more'
- Skip aria-hidden blurred placeholder experience items
- Add info callout in UI directing users to data export zip for full history
2026-03-13 19:47:21 -07:00
100add23bc chore: update changelog for v0.4.0 release
Some checks are pending
CI / test (push) Waiting to run
2026-03-13 11:28:03 -07:00
7d15980bdd docs: update backlog with LinkedIn import follow-up items
Some checks are pending
CI / test (push) Waiting to run
2026-03-13 11:24:55 -07:00
9603d591a3 fix(cloud): use per-user config dir for wizard gate; redirect on invalid session
- app.py: wizard gate now reads get_config_dir()/user.yaml instead of
  hardcoded repo-level config/ — fixes perpetual onboarding loop in
  cloud mode where per-user wizard_complete was never seen
- app.py: page title corrected to "Peregrine"
- cloud_session.py: add get_config_dir() returning per-user config path
  in cloud mode, repo config/ locally
- cloud_session.py: replace st.error() with JS redirect on missing/invalid
  session token so users land on login page instead of error screen
- Home.py, 4_Apply.py, migrate.py: remove remaining AIHawk UI references
2026-03-13 11:24:42 -07:00
f3617abb6b fix(linkedin): conservative settings merge, mkdir guard, split dockerfile playwright layer 2026-03-13 10:58:58 -07:00
6b59804d35 fix(linkedin): move session state pop before tabs; add rerun after settings merge
- Pop _linkedin_extracted before st.tabs() so tab_builder sees the
  freshly populated _parsed_resume in the same render pass (no extra rerun needed)
- Fix tab label capitalisation: "Build Manually" (capital M) per spec
- Add st.rerun() after LinkedIn merge in Settings so form fields
  refresh immediately to show the newly applied data
2026-03-13 10:55:25 -07:00
7b9e758861 feat(linkedin): install Playwright Chromium in Docker image 2026-03-13 10:44:03 -07:00
070be6c2e9 feat(linkedin): add LinkedIn import expander to Settings Resume Profile tab 2026-03-13 10:44:02 -07:00
083dff2ec8 feat(linkedin): add LinkedIn tab to wizard resume step 2026-03-13 10:43:53 -07:00
ac1db1ea7f feat(linkedin): add shared LinkedIn import Streamlit widget 2026-03-13 10:32:23 -07:00
260d186c86 feat(linkedin): add staging file parser with re-parse support 2026-03-13 10:18:01 -07:00
04d0a66f21 fix(linkedin): improve scraper error handling, current-job date range, add missing tests 2026-03-13 06:02:03 -07:00
32ed451933 feat(linkedin): add scraper (Playwright + export zip) with URL validation 2026-03-13 01:06:39 -07:00
6c61290218 feat(linkedin): add HTML parser utils with fixture tests 2026-03-13 01:01:05 -07:00
95c5a12196 feat(cloud): add Heimdall tier resolution to cloud_session
Some checks failed
CI / test (push) Has been cancelled
Calls /admin/cloud/resolve after JWT validation to inject the user's
current subscription tier (free/paid/premium/ultra) into session_state
as cloud_tier. Cached 5 minutes via st.cache_data to avoid Heimdall
spam on every Streamlit rerun. Degrades gracefully to free on timeout
or missing token.

New env vars: HEIMDALL_URL, HEIMDALL_ADMIN_TOKEN (added to .env.example
and compose.cloud.yml). HEIMDALL_URL defaults to http://cf-license:8000
for internal Docker network access.

New helper: get_cloud_tier() — returns tier string in cloud mode, "local"
in local-first mode, so pages can distinguish self-hosted from cloud.
2026-03-10 12:31:14 -07:00
cf16af05d2 fix(cloud): extract cf_session cookie by name from X-CF-Session header 2026-03-10 09:22:08 -07:00