owenkaplinsky
commited on
Commit
·
363cda9
0
Parent(s):
Clean initial commit for HuggingFace
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .github/CONTRIBUTING.md +103 -0
- .gitignore +70 -0
- Dockerfile +61 -0
- LICENSE +674 -0
- README.md +403 -0
- docker/Dockerfile.candidates_db_init +22 -0
- docker/Dockerfile.cv_upload +29 -0
- docker/Dockerfile.supervisor +36 -0
- docker/Dockerfile.supervisor_api +43 -0
- docker/Dockerfile.voice_proxy +29 -0
- docker/Dockerfile.voice_screening +28 -0
- docker/docker-compose.yml +236 -0
- docker/info.md +40 -0
- docs/agents/agent_ochestrator.md +0 -0
- docs/agents/cv_screening.md +0 -0
- docs/agents/google_mcp_agent.md +0 -0
- docs/agents/judging_agent.md +0 -0
- docs/agents/supervisor/mvps/mvp1/mvp_v1.md +1 -0
- docs/agents/supervisor/mvps/mvp2/mvp_v2.md +320 -0
- docs/agents/supervisor/mvps/mvp2/single_candidate_mvp.md +257 -0
- docs/agents/supervisor/mvps/mvp2/tools_needed.md +110 -0
- docs/agents/supervisor/mvps/overview.md +64 -0
- docs/agents/supervisor/supervisor_general.md +120 -0
- docs/agents/voice_screening.md +336 -0
- docs/context_engineering/ideas.md +33 -0
- docs/entrypoint_patterns.md +114 -0
- docs/how_langgraph_works.md +262 -0
- docs/mcp/gmail_mcp_gcp_setup.md +305 -0
- docs/mcp/google_tools_mcp.md +180 -0
- intro.md +457 -0
- langgraph.json +7 -0
- package-lock.json +6 -0
- requirements/agent.txt +3 -0
- requirements/all.txt +64 -0
- requirements/api.txt +7 -0
- requirements/base.txt +4 -0
- requirements/cv_ui.txt +15 -0
- requirements/db.txt +4 -0
- requirements/mcp_calendar.txt +15 -0
- requirements/mcp_gmail.txt +16 -0
- requirements/requirements.txt +24 -0
- requirements/supervisor.txt +12 -0
- requirements/voice_proxy.txt +10 -0
- requirements/voice_screening_ui.txt +6 -0
- scripts/__init__.py +2 -0
- scripts/db/__init__.py +10 -0
- scripts/db/debug_all.py +60 -0
- scripts/db/list_candidates.py +63 -0
- scripts/db/test_connection.py +51 -0
- scripts/db/test_session.py +40 -0
.github/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Contributing Guidelines
|
| 2 |
+
|
| 3 |
+
Welcome to the project! 🎉
|
| 4 |
+
We move fast, but we keep code quality and stability in check.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## 🏗️ Branching Model
|
| 9 |
+
|
| 10 |
+
- **main** → Always stable and demo-ready.
|
| 11 |
+
- **dev** → Integration branch.
|
| 12 |
+
- **feature/<name>** → Active development branches.
|
| 13 |
+
|
| 14 |
+
**Flow:**
|
| 15 |
+
1. Branch off `dev` for your work.
|
| 16 |
+
2. Open a Pull Request (PR) into `dev`.
|
| 17 |
+
3. Once `dev` is stable, it’s merged into `main` via a PR + review.
|
| 18 |
+
|
| 19 |
+
Example:
|
| 20 |
+
main ← dev ← feature/email-agent
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
## ✅ Pull Request Rules
|
| 26 |
+
|
| 27 |
+
- All merges into `main` or `dev` **require at least one approving review**.
|
| 28 |
+
- Keep PRs **small and focused** (<300 lines if possible).
|
| 29 |
+
- Use **clear titles** and **short descriptions** (what + why).
|
| 30 |
+
- You can **open draft PRs early** for feedback.
|
| 31 |
+
- Squash merge when possible to keep history clean.
|
| 32 |
+
|
| 33 |
+
**Naming Convention:**
|
| 34 |
+
- `feat`: add Gmail OAuth flow
|
| 35 |
+
- `fix`: handle missing image extraction
|
| 36 |
+
- `docs`: update setup instructions
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
---
|
| 40 |
+
|
| 41 |
+
## ⚙️ Branch Protection
|
| 42 |
+
|
| 43 |
+
**main**
|
| 44 |
+
- Requires PR review before merging
|
| 45 |
+
- Requires status checks to pass (CI, tests, lint)
|
| 46 |
+
- Must be up-to-date with base before merging
|
| 47 |
+
- No direct commits or force pushes
|
| 48 |
+
|
| 49 |
+
**dev**
|
| 50 |
+
- Requires PR review before merging
|
| 51 |
+
- Requires at least 1 approval
|
| 52 |
+
- Conversations must be resolved
|
| 53 |
+
- Status checks optional (for fast iteration)
|
| 54 |
+
- No direct commits or force pushes
|
| 55 |
+
- Squash or rebase merges only (no merge commits)
|
| 56 |
+
|
| 57 |
+
---
|
| 58 |
+
|
| 59 |
+
## 🚀 Quick Workflow
|
| 60 |
+
|
| 61 |
+
1. `git checkout dev`
|
| 62 |
+
2. `git pull`
|
| 63 |
+
3. `git checkout -b feature/my-new-feature`
|
| 64 |
+
4. Make your changes.
|
| 65 |
+
5. Push & open a PR → base: `dev`
|
| 66 |
+
6. Request a review.
|
| 67 |
+
7. Merge when approved and tests pass.
|
| 68 |
+
8. Once stable, open a PR from `dev → main`.
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## 🧹 Hygiene
|
| 73 |
+
|
| 74 |
+
- Delete merged branches.
|
| 75 |
+
- Use Conventional Commits for clarity.
|
| 76 |
+
- Keep `main` always deployable/demo-ready.
|
| 77 |
+
- If you break something, fix it fast 😉
|
| 78 |
+
|
| 79 |
+
---
|
| 80 |
+
|
| 81 |
+
## 💬 Reviews
|
| 82 |
+
|
| 83 |
+
- At least **one reviewer** per PR.
|
| 84 |
+
- Anyone can review — small teams move faster.
|
| 85 |
+
- **Pair reviews** encouraged for big changes.
|
| 86 |
+
- Minor changes (docs, comments) may be self-approved if trivial.
|
| 87 |
+
|
| 88 |
+
---
|
| 89 |
+
---
|
| 90 |
+
|
| 91 |
+
## ⚙️ Branch Protection Setup (GitHub Rulesets)
|
| 92 |
+
|
| 93 |
+
You can configure these under
|
| 94 |
+
➡️ **Settings → Code and automation → Rulesets**
|
| 95 |
+
|
| 96 |
+
| Branch | Require PR | Required Approvals | Require Status Checks | Require Conversation Resolution | Allow Merge Commit | Force Push | Delete Branch |
|
| 97 |
+
| :------ | :----------- | :----------------- | :-------------------- | :------------------------------ | :----------------- | :---------- | :------------- |
|
| 98 |
+
| `main` | ✅ | 1 | ✅ | ✅ | ❌ | ❌ | ❌ |
|
| 99 |
+
| `dev` | ✅ | 1 | optional | ✅ | ❌ | ❌ | ❌ |
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
|
| 103 |
+
Thanks for contributing — keep it fast, clean, and collaborative! 🚀
|
.gitignore
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# Ignore secret JSONs, not the folder
|
| 3 |
+
secrets/**/credentials.json
|
| 4 |
+
secrets/**/token.json
|
| 5 |
+
|
| 6 |
+
secrets/**/calendar_credentials.json
|
| 7 |
+
secrets/**/calendar_token.json
|
| 8 |
+
|
| 9 |
+
# (Optional) ignore any backup or tmp credentials
|
| 10 |
+
secrets/**/*_backup.json
|
| 11 |
+
|
| 12 |
+
# any log files
|
| 13 |
+
*.log
|
| 14 |
+
|
| 15 |
+
__pycache__/
|
| 16 |
+
*.pyc
|
| 17 |
+
|
| 18 |
+
.env
|
| 19 |
+
|
| 20 |
+
# pycahche files everywhere
|
| 21 |
+
**/__pycache__/
|
| 22 |
+
**/*.pyc
|
| 23 |
+
**/.DS_Store
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# data
|
| 27 |
+
src/database/cvs/uploads
|
| 28 |
+
src/database/cvs/parsed
|
| 29 |
+
src/database/voice_recordings
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
# Local credentials / tokens
|
| 34 |
+
.gcloud/
|
| 35 |
+
.env
|
| 36 |
+
.env.*
|
| 37 |
+
**/credentials.json
|
| 38 |
+
/results/
|
| 39 |
+
|
| 40 |
+
# Terraform
|
| 41 |
+
.terraform/
|
| 42 |
+
*.tfstate
|
| 43 |
+
*.tfstate.*
|
| 44 |
+
crash.log
|
| 45 |
+
terraform.tfvars
|
| 46 |
+
*.auto.tfvars
|
| 47 |
+
.override.tf
|
| 48 |
+
override.tf
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# Ignore only .pdf and .txt files in uploads and parsed folders
|
| 52 |
+
src/database/cvs/uploads/*.pdf
|
| 53 |
+
src/database/cvs/uploads/*.txt
|
| 54 |
+
src/database/cvs/parsed/*.pdf
|
| 55 |
+
src/database/cvs/parsed/*.txt
|
| 56 |
+
|
| 57 |
+
src/database/cvs/tests/*.pdf
|
| 58 |
+
src/database/cvs/tests/*.txt
|
| 59 |
+
|
| 60 |
+
# Keep these files
|
| 61 |
+
!src/database/cvs/uploads/.gitkeep
|
| 62 |
+
!src/database/cvs/parsed/.gitkeep
|
| 63 |
+
!src/database/cvs/uploads/info.md
|
| 64 |
+
!src/database/cvs/parsed/info.md
|
| 65 |
+
|
| 66 |
+
# lamnggraph CLI cache
|
| 67 |
+
.lgcache/
|
| 68 |
+
.langgraph_api/
|
| 69 |
+
|
| 70 |
+
.idea/
|
Dockerfile
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.12-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# System dependencies
|
| 6 |
+
RUN apt-get update && apt-get install -y gcc libpq-dev && rm -rf /var/lib/apt/lists/*
|
| 7 |
+
|
| 8 |
+
# Copy requirement files
|
| 9 |
+
COPY requirements/base.txt requirements/base.txt
|
| 10 |
+
COPY requirements/db.txt requirements/db.txt
|
| 11 |
+
COPY requirements/agent.txt requirements/agent.txt
|
| 12 |
+
COPY requirements/supervisor.txt requirements/supervisor.txt
|
| 13 |
+
COPY requirements/api.txt requirements/api.txt
|
| 14 |
+
COPY requirements/cv_ui.txt requirements/cv_ui.txt
|
| 15 |
+
COPY requirements/mcp_calendar.txt requirements/mcp_calendar.txt
|
| 16 |
+
COPY requirements/mcp_gmail.txt requirements/mcp_gmail.txt
|
| 17 |
+
COPY src/frontend/gradio/requirements.txt requirements/gradio.txt
|
| 18 |
+
|
| 19 |
+
# Install Python dependencies
|
| 20 |
+
RUN pip install --no-cache-dir -r requirements/base.txt \
|
| 21 |
+
&& pip install --no-cache-dir -r requirements/db.txt \
|
| 22 |
+
&& pip install --no-cache-dir -r requirements/agent.txt \
|
| 23 |
+
&& pip install --no-cache-dir -r requirements/supervisor.txt \
|
| 24 |
+
&& pip install --no-cache-dir -r requirements/api.txt \
|
| 25 |
+
&& pip install --no-cache-dir -r requirements/cv_ui.txt \
|
| 26 |
+
&& pip install --no-cache-dir -r requirements/mcp_calendar.txt \
|
| 27 |
+
&& pip install --no-cache-dir -r requirements/mcp_gmail.txt \
|
| 28 |
+
&& pip install --no-cache-dir -r requirements/gradio.txt
|
| 29 |
+
|
| 30 |
+
# Copy application code
|
| 31 |
+
COPY src/ /app/src/
|
| 32 |
+
COPY secrets/ /app/secrets/
|
| 33 |
+
|
| 34 |
+
ENV PYTHONPATH=/app
|
| 35 |
+
EXPOSE 7860
|
| 36 |
+
|
| 37 |
+
# Create entry script inside the image (avoids missing file in build context)
|
| 38 |
+
RUN printf '%s\n' \
|
| 39 |
+
'#!/usr/bin/env bash' \
|
| 40 |
+
'set -e' \
|
| 41 |
+
'' \
|
| 42 |
+
'# Hugging Face provides PORT; default to 7860 locally' \
|
| 43 |
+
'export PORT=\"${PORT:-7860}\"' \
|
| 44 |
+
'' \
|
| 45 |
+
'# Defaults for local in-container routing; can be overridden via env' \
|
| 46 |
+
'export SUPERVISOR_API_URL=\"${SUPERVISOR_API_URL:-http://127.0.0.1:8080/api/v1/supervisor}\"' \
|
| 47 |
+
'export DATABASE_API_URL=\"${DATABASE_API_URL:-http://127.0.0.1:8080/api/v1/db}\"' \
|
| 48 |
+
'export CV_UPLOAD_API_URL=\"${CV_UPLOAD_API_URL:-http://127.0.0.1:8080/api/v1/cv}\"' \
|
| 49 |
+
'' \
|
| 50 |
+
'# Start FastAPI backend' \
|
| 51 |
+
'uvicorn src.api.app:app --host 0.0.0.0 --port 8080 &' \
|
| 52 |
+
'' \
|
| 53 |
+
'# Give the API a moment to come up' \
|
| 54 |
+
'sleep 2' \
|
| 55 |
+
'' \
|
| 56 |
+
'# Run Gradio frontend' \
|
| 57 |
+
'python src/frontend/gradio/app.py' \
|
| 58 |
+
> /app/start.sh \
|
| 59 |
+
&& chmod +x /app/start.sh
|
| 60 |
+
|
| 61 |
+
CMD ["/app/start.sh"]
|
LICENSE
ADDED
|
@@ -0,0 +1,674 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
GNU GENERAL PUBLIC LICENSE
|
| 2 |
+
Version 3, 29 June 2007
|
| 3 |
+
|
| 4 |
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
| 5 |
+
Everyone is permitted to copy and distribute verbatim copies
|
| 6 |
+
of this license document, but changing it is not allowed.
|
| 7 |
+
|
| 8 |
+
Preamble
|
| 9 |
+
|
| 10 |
+
The GNU General Public License is a free, copyleft license for
|
| 11 |
+
software and other kinds of works.
|
| 12 |
+
|
| 13 |
+
The licenses for most software and other practical works are designed
|
| 14 |
+
to take away your freedom to share and change the works. By contrast,
|
| 15 |
+
the GNU General Public License is intended to guarantee your freedom to
|
| 16 |
+
share and change all versions of a program--to make sure it remains free
|
| 17 |
+
software for all its users. We, the Free Software Foundation, use the
|
| 18 |
+
GNU General Public License for most of our software; it applies also to
|
| 19 |
+
any other work released this way by its authors. You can apply it to
|
| 20 |
+
your programs, too.
|
| 21 |
+
|
| 22 |
+
When we speak of free software, we are referring to freedom, not
|
| 23 |
+
price. Our General Public Licenses are designed to make sure that you
|
| 24 |
+
have the freedom to distribute copies of free software (and charge for
|
| 25 |
+
them if you wish), that you receive source code or can get it if you
|
| 26 |
+
want it, that you can change the software or use pieces of it in new
|
| 27 |
+
free programs, and that you know you can do these things.
|
| 28 |
+
|
| 29 |
+
To protect your rights, we need to prevent others from denying you
|
| 30 |
+
these rights or asking you to surrender the rights. Therefore, you have
|
| 31 |
+
certain responsibilities if you distribute copies of the software, or if
|
| 32 |
+
you modify it: responsibilities to respect the freedom of others.
|
| 33 |
+
|
| 34 |
+
For example, if you distribute copies of such a program, whether
|
| 35 |
+
gratis or for a fee, you must pass on to the recipients the same
|
| 36 |
+
freedoms that you received. You must make sure that they, too, receive
|
| 37 |
+
or can get the source code. And you must show them these terms so they
|
| 38 |
+
know their rights.
|
| 39 |
+
|
| 40 |
+
Developers that use the GNU GPL protect your rights with two steps:
|
| 41 |
+
(1) assert copyright on the software, and (2) offer you this License
|
| 42 |
+
giving you legal permission to copy, distribute and/or modify it.
|
| 43 |
+
|
| 44 |
+
For the developers' and authors' protection, the GPL clearly explains
|
| 45 |
+
that there is no warranty for this free software. For both users' and
|
| 46 |
+
authors' sake, the GPL requires that modified versions be marked as
|
| 47 |
+
changed, so that their problems will not be attributed erroneously to
|
| 48 |
+
authors of previous versions.
|
| 49 |
+
|
| 50 |
+
Some devices are designed to deny users access to install or run
|
| 51 |
+
modified versions of the software inside them, although the manufacturer
|
| 52 |
+
can do so. This is fundamentally incompatible with the aim of
|
| 53 |
+
protecting users' freedom to change the software. The systematic
|
| 54 |
+
pattern of such abuse occurs in the area of products for individuals to
|
| 55 |
+
use, which is precisely where it is most unacceptable. Therefore, we
|
| 56 |
+
have designed this version of the GPL to prohibit the practice for those
|
| 57 |
+
products. If such problems arise substantially in other domains, we
|
| 58 |
+
stand ready to extend this provision to those domains in future versions
|
| 59 |
+
of the GPL, as needed to protect the freedom of users.
|
| 60 |
+
|
| 61 |
+
Finally, every program is threatened constantly by software patents.
|
| 62 |
+
States should not allow patents to restrict development and use of
|
| 63 |
+
software on general-purpose computers, but in those that do, we wish to
|
| 64 |
+
avoid the special danger that patents applied to a free program could
|
| 65 |
+
make it effectively proprietary. To prevent this, the GPL assures that
|
| 66 |
+
patents cannot be used to render the program non-free.
|
| 67 |
+
|
| 68 |
+
The precise terms and conditions for copying, distribution and
|
| 69 |
+
modification follow.
|
| 70 |
+
|
| 71 |
+
TERMS AND CONDITIONS
|
| 72 |
+
|
| 73 |
+
0. Definitions.
|
| 74 |
+
|
| 75 |
+
"This License" refers to version 3 of the GNU General Public License.
|
| 76 |
+
|
| 77 |
+
"Copyright" also means copyright-like laws that apply to other kinds of
|
| 78 |
+
works, such as semiconductor masks.
|
| 79 |
+
|
| 80 |
+
"The Program" refers to any copyrightable work licensed under this
|
| 81 |
+
License. Each licensee is addressed as "you". "Licensees" and
|
| 82 |
+
"recipients" may be individuals or organizations.
|
| 83 |
+
|
| 84 |
+
To "modify" a work means to copy from or adapt all or part of the work
|
| 85 |
+
in a fashion requiring copyright permission, other than the making of an
|
| 86 |
+
exact copy. The resulting work is called a "modified version" of the
|
| 87 |
+
earlier work or a work "based on" the earlier work.
|
| 88 |
+
|
| 89 |
+
A "covered work" means either the unmodified Program or a work based
|
| 90 |
+
on the Program.
|
| 91 |
+
|
| 92 |
+
To "propagate" a work means to do anything with it that, without
|
| 93 |
+
permission, would make you directly or secondarily liable for
|
| 94 |
+
infringement under applicable copyright law, except executing it on a
|
| 95 |
+
computer or modifying a private copy. Propagation includes copying,
|
| 96 |
+
distribution (with or without modification), making available to the
|
| 97 |
+
public, and in some countries other activities as well.
|
| 98 |
+
|
| 99 |
+
To "convey" a work means any kind of propagation that enables other
|
| 100 |
+
parties to make or receive copies. Mere interaction with a user through
|
| 101 |
+
a computer network, with no transfer of a copy, is not conveying.
|
| 102 |
+
|
| 103 |
+
An interactive user interface displays "Appropriate Legal Notices"
|
| 104 |
+
to the extent that it includes a convenient and prominently visible
|
| 105 |
+
feature that (1) displays an appropriate copyright notice, and (2)
|
| 106 |
+
tells the user that there is no warranty for the work (except to the
|
| 107 |
+
extent that warranties are provided), that licensees may convey the
|
| 108 |
+
work under this License, and how to view a copy of this License. If
|
| 109 |
+
the interface presents a list of user commands or options, such as a
|
| 110 |
+
menu, a prominent item in the list meets this criterion.
|
| 111 |
+
|
| 112 |
+
1. Source Code.
|
| 113 |
+
|
| 114 |
+
The "source code" for a work means the preferred form of the work
|
| 115 |
+
for making modifications to it. "Object code" means any non-source
|
| 116 |
+
form of a work.
|
| 117 |
+
|
| 118 |
+
A "Standard Interface" means an interface that either is an official
|
| 119 |
+
standard defined by a recognized standards body, or, in the case of
|
| 120 |
+
interfaces specified for a particular programming language, one that
|
| 121 |
+
is widely used among developers working in that language.
|
| 122 |
+
|
| 123 |
+
The "System Libraries" of an executable work include anything, other
|
| 124 |
+
than the work as a whole, that (a) is included in the normal form of
|
| 125 |
+
packaging a Major Component, but which is not part of that Major
|
| 126 |
+
Component, and (b) serves only to enable use of the work with that
|
| 127 |
+
Major Component, or to implement a Standard Interface for which an
|
| 128 |
+
implementation is available to the public in source code form. A
|
| 129 |
+
"Major Component", in this context, means a major essential component
|
| 130 |
+
(kernel, window system, and so on) of the specific operating system
|
| 131 |
+
(if any) on which the executable work runs, or a compiler used to
|
| 132 |
+
produce the work, or an object code interpreter used to run it.
|
| 133 |
+
|
| 134 |
+
The "Corresponding Source" for a work in object code form means all
|
| 135 |
+
the source code needed to generate, install, and (for an executable
|
| 136 |
+
work) run the object code and to modify the work, including scripts to
|
| 137 |
+
control those activities. However, it does not include the work's
|
| 138 |
+
System Libraries, or general-purpose tools or generally available free
|
| 139 |
+
programs which are used unmodified in performing those activities but
|
| 140 |
+
which are not part of the work. For example, Corresponding Source
|
| 141 |
+
includes interface definition files associated with source files for
|
| 142 |
+
the work, and the source code for shared libraries and dynamically
|
| 143 |
+
linked subprograms that the work is specifically designed to require,
|
| 144 |
+
such as by intimate data communication or control flow between those
|
| 145 |
+
subprograms and other parts of the work.
|
| 146 |
+
|
| 147 |
+
The Corresponding Source need not include anything that users
|
| 148 |
+
can regenerate automatically from other parts of the Corresponding
|
| 149 |
+
Source.
|
| 150 |
+
|
| 151 |
+
The Corresponding Source for a work in source code form is that
|
| 152 |
+
same work.
|
| 153 |
+
|
| 154 |
+
2. Basic Permissions.
|
| 155 |
+
|
| 156 |
+
All rights granted under this License are granted for the term of
|
| 157 |
+
copyright on the Program, and are irrevocable provided the stated
|
| 158 |
+
conditions are met. This License explicitly affirms your unlimited
|
| 159 |
+
permission to run the unmodified Program. The output from running a
|
| 160 |
+
covered work is covered by this License only if the output, given its
|
| 161 |
+
content, constitutes a covered work. This License acknowledges your
|
| 162 |
+
rights of fair use or other equivalent, as provided by copyright law.
|
| 163 |
+
|
| 164 |
+
You may make, run and propagate covered works that you do not
|
| 165 |
+
convey, without conditions so long as your license otherwise remains
|
| 166 |
+
in force. You may convey covered works to others for the sole purpose
|
| 167 |
+
of having them make modifications exclusively for you, or provide you
|
| 168 |
+
with facilities for running those works, provided that you comply with
|
| 169 |
+
the terms of this License in conveying all material for which you do
|
| 170 |
+
not control copyright. Those thus making or running the covered works
|
| 171 |
+
for you must do so exclusively on your behalf, under your direction
|
| 172 |
+
and control, on terms that prohibit them from making any copies of
|
| 173 |
+
your copyrighted material outside their relationship with you.
|
| 174 |
+
|
| 175 |
+
Conveying under any other circumstances is permitted solely under
|
| 176 |
+
the conditions stated below. Sublicensing is not allowed; section 10
|
| 177 |
+
makes it unnecessary.
|
| 178 |
+
|
| 179 |
+
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
| 180 |
+
|
| 181 |
+
No covered work shall be deemed part of an effective technological
|
| 182 |
+
measure under any applicable law fulfilling obligations under article
|
| 183 |
+
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
| 184 |
+
similar laws prohibiting or restricting circumvention of such
|
| 185 |
+
measures.
|
| 186 |
+
|
| 187 |
+
When you convey a covered work, you waive any legal power to forbid
|
| 188 |
+
circumvention of technological measures to the extent such circumvention
|
| 189 |
+
is effected by exercising rights under this License with respect to
|
| 190 |
+
the covered work, and you disclaim any intention to limit operation or
|
| 191 |
+
modification of the work as a means of enforcing, against the work's
|
| 192 |
+
users, your or third parties' legal rights to forbid circumvention of
|
| 193 |
+
technological measures.
|
| 194 |
+
|
| 195 |
+
4. Conveying Verbatim Copies.
|
| 196 |
+
|
| 197 |
+
You may convey verbatim copies of the Program's source code as you
|
| 198 |
+
receive it, in any medium, provided that you conspicuously and
|
| 199 |
+
appropriately publish on each copy an appropriate copyright notice;
|
| 200 |
+
keep intact all notices stating that this License and any
|
| 201 |
+
non-permissive terms added in accord with section 7 apply to the code;
|
| 202 |
+
keep intact all notices of the absence of any warranty; and give all
|
| 203 |
+
recipients a copy of this License along with the Program.
|
| 204 |
+
|
| 205 |
+
You may charge any price or no price for each copy that you convey,
|
| 206 |
+
and you may offer support or warranty protection for a fee.
|
| 207 |
+
|
| 208 |
+
5. Conveying Modified Source Versions.
|
| 209 |
+
|
| 210 |
+
You may convey a work based on the Program, or the modifications to
|
| 211 |
+
produce it from the Program, in the form of source code under the
|
| 212 |
+
terms of section 4, provided that you also meet all of these conditions:
|
| 213 |
+
|
| 214 |
+
a) The work must carry prominent notices stating that you modified
|
| 215 |
+
it, and giving a relevant date.
|
| 216 |
+
|
| 217 |
+
b) The work must carry prominent notices stating that it is
|
| 218 |
+
released under this License and any conditions added under section
|
| 219 |
+
7. This requirement modifies the requirement in section 4 to
|
| 220 |
+
"keep intact all notices".
|
| 221 |
+
|
| 222 |
+
c) You must license the entire work, as a whole, under this
|
| 223 |
+
License to anyone who comes into possession of a copy. This
|
| 224 |
+
License will therefore apply, along with any applicable section 7
|
| 225 |
+
additional terms, to the whole of the work, and all its parts,
|
| 226 |
+
regardless of how they are packaged. This License gives no
|
| 227 |
+
permission to license the work in any other way, but it does not
|
| 228 |
+
invalidate such permission if you have separately received it.
|
| 229 |
+
|
| 230 |
+
d) If the work has interactive user interfaces, each must display
|
| 231 |
+
Appropriate Legal Notices; however, if the Program has interactive
|
| 232 |
+
interfaces that do not display Appropriate Legal Notices, your
|
| 233 |
+
work need not make them do so.
|
| 234 |
+
|
| 235 |
+
A compilation of a covered work with other separate and independent
|
| 236 |
+
works, which are not by their nature extensions of the covered work,
|
| 237 |
+
and which are not combined with it such as to form a larger program,
|
| 238 |
+
in or on a volume of a storage or distribution medium, is called an
|
| 239 |
+
"aggregate" if the compilation and its resulting copyright are not
|
| 240 |
+
used to limit the access or legal rights of the compilation's users
|
| 241 |
+
beyond what the individual works permit. Inclusion of a covered work
|
| 242 |
+
in an aggregate does not cause this License to apply to the other
|
| 243 |
+
parts of the aggregate.
|
| 244 |
+
|
| 245 |
+
6. Conveying Non-Source Forms.
|
| 246 |
+
|
| 247 |
+
You may convey a covered work in object code form under the terms
|
| 248 |
+
of sections 4 and 5, provided that you also convey the
|
| 249 |
+
machine-readable Corresponding Source under the terms of this License,
|
| 250 |
+
in one of these ways:
|
| 251 |
+
|
| 252 |
+
a) Convey the object code in, or embodied in, a physical product
|
| 253 |
+
(including a physical distribution medium), accompanied by the
|
| 254 |
+
Corresponding Source fixed on a durable physical medium
|
| 255 |
+
customarily used for software interchange.
|
| 256 |
+
|
| 257 |
+
b) Convey the object code in, or embodied in, a physical product
|
| 258 |
+
(including a physical distribution medium), accompanied by a
|
| 259 |
+
written offer, valid for at least three years and valid for as
|
| 260 |
+
long as you offer spare parts or customer support for that product
|
| 261 |
+
model, to give anyone who possesses the object code either (1) a
|
| 262 |
+
copy of the Corresponding Source for all the software in the
|
| 263 |
+
product that is covered by this License, on a durable physical
|
| 264 |
+
medium customarily used for software interchange, for a price no
|
| 265 |
+
more than your reasonable cost of physically performing this
|
| 266 |
+
conveying of source, or (2) access to copy the
|
| 267 |
+
Corresponding Source from a network server at no charge.
|
| 268 |
+
|
| 269 |
+
c) Convey individual copies of the object code with a copy of the
|
| 270 |
+
written offer to provide the Corresponding Source. This
|
| 271 |
+
alternative is allowed only occasionally and noncommercially, and
|
| 272 |
+
only if you received the object code with such an offer, in accord
|
| 273 |
+
with subsection 6b.
|
| 274 |
+
|
| 275 |
+
d) Convey the object code by offering access from a designated
|
| 276 |
+
place (gratis or for a charge), and offer equivalent access to the
|
| 277 |
+
Corresponding Source in the same way through the same place at no
|
| 278 |
+
further charge. You need not require recipients to copy the
|
| 279 |
+
Corresponding Source along with the object code. If the place to
|
| 280 |
+
copy the object code is a network server, the Corresponding Source
|
| 281 |
+
may be on a different server (operated by you or a third party)
|
| 282 |
+
that supports equivalent copying facilities, provided you maintain
|
| 283 |
+
clear directions next to the object code saying where to find the
|
| 284 |
+
Corresponding Source. Regardless of what server hosts the
|
| 285 |
+
Corresponding Source, you remain obligated to ensure that it is
|
| 286 |
+
available for as long as needed to satisfy these requirements.
|
| 287 |
+
|
| 288 |
+
e) Convey the object code using peer-to-peer transmission, provided
|
| 289 |
+
you inform other peers where the object code and Corresponding
|
| 290 |
+
Source of the work are being offered to the general public at no
|
| 291 |
+
charge under subsection 6d.
|
| 292 |
+
|
| 293 |
+
A separable portion of the object code, whose source code is excluded
|
| 294 |
+
from the Corresponding Source as a System Library, need not be
|
| 295 |
+
included in conveying the object code work.
|
| 296 |
+
|
| 297 |
+
A "User Product" is either (1) a "consumer product", which means any
|
| 298 |
+
tangible personal property which is normally used for personal, family,
|
| 299 |
+
or household purposes, or (2) anything designed or sold for incorporation
|
| 300 |
+
into a dwelling. In determining whether a product is a consumer product,
|
| 301 |
+
doubtful cases shall be resolved in favor of coverage. For a particular
|
| 302 |
+
product received by a particular user, "normally used" refers to a
|
| 303 |
+
typical or common use of that class of product, regardless of the status
|
| 304 |
+
of the particular user or of the way in which the particular user
|
| 305 |
+
actually uses, or expects or is expected to use, the product. A product
|
| 306 |
+
is a consumer product regardless of whether the product has substantial
|
| 307 |
+
commercial, industrial or non-consumer uses, unless such uses represent
|
| 308 |
+
the only significant mode of use of the product.
|
| 309 |
+
|
| 310 |
+
"Installation Information" for a User Product means any methods,
|
| 311 |
+
procedures, authorization keys, or other information required to install
|
| 312 |
+
and execute modified versions of a covered work in that User Product from
|
| 313 |
+
a modified version of its Corresponding Source. The information must
|
| 314 |
+
suffice to ensure that the continued functioning of the modified object
|
| 315 |
+
code is in no case prevented or interfered with solely because
|
| 316 |
+
modification has been made.
|
| 317 |
+
|
| 318 |
+
If you convey an object code work under this section in, or with, or
|
| 319 |
+
specifically for use in, a User Product, and the conveying occurs as
|
| 320 |
+
part of a transaction in which the right of possession and use of the
|
| 321 |
+
User Product is transferred to the recipient in perpetuity or for a
|
| 322 |
+
fixed term (regardless of how the transaction is characterized), the
|
| 323 |
+
Corresponding Source conveyed under this section must be accompanied
|
| 324 |
+
by the Installation Information. But this requirement does not apply
|
| 325 |
+
if neither you nor any third party retains the ability to install
|
| 326 |
+
modified object code on the User Product (for example, the work has
|
| 327 |
+
been installed in ROM).
|
| 328 |
+
|
| 329 |
+
The requirement to provide Installation Information does not include a
|
| 330 |
+
requirement to continue to provide support service, warranty, or updates
|
| 331 |
+
for a work that has been modified or installed by the recipient, or for
|
| 332 |
+
the User Product in which it has been modified or installed. Access to a
|
| 333 |
+
network may be denied when the modification itself materially and
|
| 334 |
+
adversely affects the operation of the network or violates the rules and
|
| 335 |
+
protocols for communication across the network.
|
| 336 |
+
|
| 337 |
+
Corresponding Source conveyed, and Installation Information provided,
|
| 338 |
+
in accord with this section must be in a format that is publicly
|
| 339 |
+
documented (and with an implementation available to the public in
|
| 340 |
+
source code form), and must require no special password or key for
|
| 341 |
+
unpacking, reading or copying.
|
| 342 |
+
|
| 343 |
+
7. Additional Terms.
|
| 344 |
+
|
| 345 |
+
"Additional permissions" are terms that supplement the terms of this
|
| 346 |
+
License by making exceptions from one or more of its conditions.
|
| 347 |
+
Additional permissions that are applicable to the entire Program shall
|
| 348 |
+
be treated as though they were included in this License, to the extent
|
| 349 |
+
that they are valid under applicable law. If additional permissions
|
| 350 |
+
apply only to part of the Program, that part may be used separately
|
| 351 |
+
under those permissions, but the entire Program remains governed by
|
| 352 |
+
this License without regard to the additional permissions.
|
| 353 |
+
|
| 354 |
+
When you convey a copy of a covered work, you may at your option
|
| 355 |
+
remove any additional permissions from that copy, or from any part of
|
| 356 |
+
it. (Additional permissions may be written to require their own
|
| 357 |
+
removal in certain cases when you modify the work.) You may place
|
| 358 |
+
additional permissions on material, added by you to a covered work,
|
| 359 |
+
for which you have or can give appropriate copyright permission.
|
| 360 |
+
|
| 361 |
+
Notwithstanding any other provision of this License, for material you
|
| 362 |
+
add to a covered work, you may (if authorized by the copyright holders of
|
| 363 |
+
that material) supplement the terms of this License with terms:
|
| 364 |
+
|
| 365 |
+
a) Disclaiming warranty or limiting liability differently from the
|
| 366 |
+
terms of sections 15 and 16 of this License; or
|
| 367 |
+
|
| 368 |
+
b) Requiring preservation of specified reasonable legal notices or
|
| 369 |
+
author attributions in that material or in the Appropriate Legal
|
| 370 |
+
Notices displayed by works containing it; or
|
| 371 |
+
|
| 372 |
+
c) Prohibiting misrepresentation of the origin of that material, or
|
| 373 |
+
requiring that modified versions of such material be marked in
|
| 374 |
+
reasonable ways as different from the original version; or
|
| 375 |
+
|
| 376 |
+
d) Limiting the use for publicity purposes of names of licensors or
|
| 377 |
+
authors of the material; or
|
| 378 |
+
|
| 379 |
+
e) Declining to grant rights under trademark law for use of some
|
| 380 |
+
trade names, trademarks, or service marks; or
|
| 381 |
+
|
| 382 |
+
f) Requiring indemnification of licensors and authors of that
|
| 383 |
+
material by anyone who conveys the material (or modified versions of
|
| 384 |
+
it) with contractual assumptions of liability to the recipient, for
|
| 385 |
+
any liability that these contractual assumptions directly impose on
|
| 386 |
+
those licensors and authors.
|
| 387 |
+
|
| 388 |
+
All other non-permissive additional terms are considered "further
|
| 389 |
+
restrictions" within the meaning of section 10. If the Program as you
|
| 390 |
+
received it, or any part of it, contains a notice stating that it is
|
| 391 |
+
governed by this License along with a term that is a further
|
| 392 |
+
restriction, you may remove that term. If a license document contains
|
| 393 |
+
a further restriction but permits relicensing or conveying under this
|
| 394 |
+
License, you may add to a covered work material governed by the terms
|
| 395 |
+
of that license document, provided that the further restriction does
|
| 396 |
+
not survive such relicensing or conveying.
|
| 397 |
+
|
| 398 |
+
If you add terms to a covered work in accord with this section, you
|
| 399 |
+
must place, in the relevant source files, a statement of the
|
| 400 |
+
additional terms that apply to those files, or a notice indicating
|
| 401 |
+
where to find the applicable terms.
|
| 402 |
+
|
| 403 |
+
Additional terms, permissive or non-permissive, may be stated in the
|
| 404 |
+
form of a separately written license, or stated as exceptions;
|
| 405 |
+
the above requirements apply either way.
|
| 406 |
+
|
| 407 |
+
8. Termination.
|
| 408 |
+
|
| 409 |
+
You may not propagate or modify a covered work except as expressly
|
| 410 |
+
provided under this License. Any attempt otherwise to propagate or
|
| 411 |
+
modify it is void, and will automatically terminate your rights under
|
| 412 |
+
this License (including any patent licenses granted under the third
|
| 413 |
+
paragraph of section 11).
|
| 414 |
+
|
| 415 |
+
However, if you cease all violation of this License, then your
|
| 416 |
+
license from a particular copyright holder is reinstated (a)
|
| 417 |
+
provisionally, unless and until the copyright holder explicitly and
|
| 418 |
+
finally terminates your license, and (b) permanently, if the copyright
|
| 419 |
+
holder fails to notify you of the violation by some reasonable means
|
| 420 |
+
prior to 60 days after the cessation.
|
| 421 |
+
|
| 422 |
+
Moreover, your license from a particular copyright holder is
|
| 423 |
+
reinstated permanently if the copyright holder notifies you of the
|
| 424 |
+
violation by some reasonable means, this is the first time you have
|
| 425 |
+
received notice of violation of this License (for any work) from that
|
| 426 |
+
copyright holder, and you cure the violation prior to 30 days after
|
| 427 |
+
your receipt of the notice.
|
| 428 |
+
|
| 429 |
+
Termination of your rights under this section does not terminate the
|
| 430 |
+
licenses of parties who have received copies or rights from you under
|
| 431 |
+
this License. If your rights have been terminated and not permanently
|
| 432 |
+
reinstated, you do not qualify to receive new licenses for the same
|
| 433 |
+
material under section 10.
|
| 434 |
+
|
| 435 |
+
9. Acceptance Not Required for Having Copies.
|
| 436 |
+
|
| 437 |
+
You are not required to accept this License in order to receive or
|
| 438 |
+
run a copy of the Program. Ancillary propagation of a covered work
|
| 439 |
+
occurring solely as a consequence of using peer-to-peer transmission
|
| 440 |
+
to receive a copy likewise does not require acceptance. However,
|
| 441 |
+
nothing other than this License grants you permission to propagate or
|
| 442 |
+
modify any covered work. These actions infringe copyright if you do
|
| 443 |
+
not accept this License. Therefore, by modifying or propagating a
|
| 444 |
+
covered work, you indicate your acceptance of this License to do so.
|
| 445 |
+
|
| 446 |
+
10. Automatic Licensing of Downstream Recipients.
|
| 447 |
+
|
| 448 |
+
Each time you convey a covered work, the recipient automatically
|
| 449 |
+
receives a license from the original licensors, to run, modify and
|
| 450 |
+
propagate that work, subject to this License. You are not responsible
|
| 451 |
+
for enforcing compliance by third parties with this License.
|
| 452 |
+
|
| 453 |
+
An "entity transaction" is a transaction transferring control of an
|
| 454 |
+
organization, or substantially all assets of one, or subdividing an
|
| 455 |
+
organization, or merging organizations. If propagation of a covered
|
| 456 |
+
work results from an entity transaction, each party to that
|
| 457 |
+
transaction who receives a copy of the work also receives whatever
|
| 458 |
+
licenses to the work the party's predecessor in interest had or could
|
| 459 |
+
give under the previous paragraph, plus a right to possession of the
|
| 460 |
+
Corresponding Source of the work from the predecessor in interest, if
|
| 461 |
+
the predecessor has it or can get it with reasonable efforts.
|
| 462 |
+
|
| 463 |
+
You may not impose any further restrictions on the exercise of the
|
| 464 |
+
rights granted or affirmed under this License. For example, you may
|
| 465 |
+
not impose a license fee, royalty, or other charge for exercise of
|
| 466 |
+
rights granted under this License, and you may not initiate litigation
|
| 467 |
+
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
| 468 |
+
any patent claim is infringed by making, using, selling, offering for
|
| 469 |
+
sale, or importing the Program or any portion of it.
|
| 470 |
+
|
| 471 |
+
11. Patents.
|
| 472 |
+
|
| 473 |
+
A "contributor" is a copyright holder who authorizes use under this
|
| 474 |
+
License of the Program or a work on which the Program is based. The
|
| 475 |
+
work thus licensed is called the contributor's "contributor version".
|
| 476 |
+
|
| 477 |
+
A contributor's "essential patent claims" are all patent claims
|
| 478 |
+
owned or controlled by the contributor, whether already acquired or
|
| 479 |
+
hereafter acquired, that would be infringed by some manner, permitted
|
| 480 |
+
by this License, of making, using, or selling its contributor version,
|
| 481 |
+
but do not include claims that would be infringed only as a
|
| 482 |
+
consequence of further modification of the contributor version. For
|
| 483 |
+
purposes of this definition, "control" includes the right to grant
|
| 484 |
+
patent sublicenses in a manner consistent with the requirements of
|
| 485 |
+
this License.
|
| 486 |
+
|
| 487 |
+
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
| 488 |
+
patent license under the contributor's essential patent claims, to
|
| 489 |
+
make, use, sell, offer for sale, import and otherwise run, modify and
|
| 490 |
+
propagate the contents of its contributor version.
|
| 491 |
+
|
| 492 |
+
In the following three paragraphs, a "patent license" is any express
|
| 493 |
+
agreement or commitment, however denominated, not to enforce a patent
|
| 494 |
+
(such as an express permission to practice a patent or covenant not to
|
| 495 |
+
sue for patent infringement). To "grant" such a patent license to a
|
| 496 |
+
party means to make such an agreement or commitment not to enforce a
|
| 497 |
+
patent against the party.
|
| 498 |
+
|
| 499 |
+
If you convey a covered work, knowingly relying on a patent license,
|
| 500 |
+
and the Corresponding Source of the work is not available for anyone
|
| 501 |
+
to copy, free of charge and under the terms of this License, through a
|
| 502 |
+
publicly available network server or other readily accessible means,
|
| 503 |
+
then you must either (1) cause the Corresponding Source to be so
|
| 504 |
+
available, or (2) arrange to deprive yourself of the benefit of the
|
| 505 |
+
patent license for this particular work, or (3) arrange, in a manner
|
| 506 |
+
consistent with the requirements of this License, to extend the patent
|
| 507 |
+
license to downstream recipients. "Knowingly relying" means you have
|
| 508 |
+
actual knowledge that, but for the patent license, your conveying the
|
| 509 |
+
covered work in a country, or your recipient's use of the covered work
|
| 510 |
+
in a country, would infringe one or more identifiable patents in that
|
| 511 |
+
country that you have reason to believe are valid.
|
| 512 |
+
|
| 513 |
+
If, pursuant to or in connection with a single transaction or
|
| 514 |
+
arrangement, you convey, or propagate by procuring conveyance of, a
|
| 515 |
+
covered work, and grant a patent license to some of the parties
|
| 516 |
+
receiving the covered work authorizing them to use, propagate, modify
|
| 517 |
+
or convey a specific copy of the covered work, then the patent license
|
| 518 |
+
you grant is automatically extended to all recipients of the covered
|
| 519 |
+
work and works based on it.
|
| 520 |
+
|
| 521 |
+
A patent license is "discriminatory" if it does not include within
|
| 522 |
+
the scope of its coverage, prohibits the exercise of, or is
|
| 523 |
+
conditioned on the non-exercise of one or more of the rights that are
|
| 524 |
+
specifically granted under this License. You may not convey a covered
|
| 525 |
+
work if you are a party to an arrangement with a third party that is
|
| 526 |
+
in the business of distributing software, under which you make payment
|
| 527 |
+
to the third party based on the extent of your activity of conveying
|
| 528 |
+
the work, and under which the third party grants, to any of the
|
| 529 |
+
parties who would receive the covered work from you, a discriminatory
|
| 530 |
+
patent license (a) in connection with copies of the covered work
|
| 531 |
+
conveyed by you (or copies made from those copies), or (b) primarily
|
| 532 |
+
for and in connection with specific products or compilations that
|
| 533 |
+
contain the covered work, unless you entered into that arrangement,
|
| 534 |
+
or that patent license was granted, prior to 28 March 2007.
|
| 535 |
+
|
| 536 |
+
Nothing in this License shall be construed as excluding or limiting
|
| 537 |
+
any implied license or other defenses to infringement that may
|
| 538 |
+
otherwise be available to you under applicable patent law.
|
| 539 |
+
|
| 540 |
+
12. No Surrender of Others' Freedom.
|
| 541 |
+
|
| 542 |
+
If conditions are imposed on you (whether by court order, agreement or
|
| 543 |
+
otherwise) that contradict the conditions of this License, they do not
|
| 544 |
+
excuse you from the conditions of this License. If you cannot convey a
|
| 545 |
+
covered work so as to satisfy simultaneously your obligations under this
|
| 546 |
+
License and any other pertinent obligations, then as a consequence you may
|
| 547 |
+
not convey it at all. For example, if you agree to terms that obligate you
|
| 548 |
+
to collect a royalty for further conveying from those to whom you convey
|
| 549 |
+
the Program, the only way you could satisfy both those terms and this
|
| 550 |
+
License would be to refrain entirely from conveying the Program.
|
| 551 |
+
|
| 552 |
+
13. Use with the GNU Affero General Public License.
|
| 553 |
+
|
| 554 |
+
Notwithstanding any other provision of this License, you have
|
| 555 |
+
permission to link or combine any covered work with a work licensed
|
| 556 |
+
under version 3 of the GNU Affero General Public License into a single
|
| 557 |
+
combined work, and to convey the resulting work. The terms of this
|
| 558 |
+
License will continue to apply to the part which is the covered work,
|
| 559 |
+
but the special requirements of the GNU Affero General Public License,
|
| 560 |
+
section 13, concerning interaction through a network will apply to the
|
| 561 |
+
combination as such.
|
| 562 |
+
|
| 563 |
+
14. Revised Versions of this License.
|
| 564 |
+
|
| 565 |
+
The Free Software Foundation may publish revised and/or new versions of
|
| 566 |
+
the GNU General Public License from time to time. Such new versions will
|
| 567 |
+
be similar in spirit to the present version, but may differ in detail to
|
| 568 |
+
address new problems or concerns.
|
| 569 |
+
|
| 570 |
+
Each version is given a distinguishing version number. If the
|
| 571 |
+
Program specifies that a certain numbered version of the GNU General
|
| 572 |
+
Public License "or any later version" applies to it, you have the
|
| 573 |
+
option of following the terms and conditions either of that numbered
|
| 574 |
+
version or of any later version published by the Free Software
|
| 575 |
+
Foundation. If the Program does not specify a version number of the
|
| 576 |
+
GNU General Public License, you may choose any version ever published
|
| 577 |
+
by the Free Software Foundation.
|
| 578 |
+
|
| 579 |
+
If the Program specifies that a proxy can decide which future
|
| 580 |
+
versions of the GNU General Public License can be used, that proxy's
|
| 581 |
+
public statement of acceptance of a version permanently authorizes you
|
| 582 |
+
to choose that version for the Program.
|
| 583 |
+
|
| 584 |
+
Later license versions may give you additional or different
|
| 585 |
+
permissions. However, no additional obligations are imposed on any
|
| 586 |
+
author or copyright holder as a result of your choosing to follow a
|
| 587 |
+
later version.
|
| 588 |
+
|
| 589 |
+
15. Disclaimer of Warranty.
|
| 590 |
+
|
| 591 |
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
| 592 |
+
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
| 593 |
+
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
| 594 |
+
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
| 595 |
+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
| 596 |
+
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
| 597 |
+
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
| 598 |
+
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
| 599 |
+
|
| 600 |
+
16. Limitation of Liability.
|
| 601 |
+
|
| 602 |
+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
| 603 |
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
| 604 |
+
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
| 605 |
+
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
| 606 |
+
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
| 607 |
+
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
| 608 |
+
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
| 609 |
+
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
| 610 |
+
SUCH DAMAGES.
|
| 611 |
+
|
| 612 |
+
17. Interpretation of Sections 15 and 16.
|
| 613 |
+
|
| 614 |
+
If the disclaimer of warranty and limitation of liability provided
|
| 615 |
+
above cannot be given local legal effect according to their terms,
|
| 616 |
+
reviewing courts shall apply local law that most closely approximates
|
| 617 |
+
an absolute waiver of all civil liability in connection with the
|
| 618 |
+
Program, unless a warranty or assumption of liability accompanies a
|
| 619 |
+
copy of the Program in return for a fee.
|
| 620 |
+
|
| 621 |
+
END OF TERMS AND CONDITIONS
|
| 622 |
+
|
| 623 |
+
How to Apply These Terms to Your New Programs
|
| 624 |
+
|
| 625 |
+
If you develop a new program, and you want it to be of the greatest
|
| 626 |
+
possible use to the public, the best way to achieve this is to make it
|
| 627 |
+
free software which everyone can redistribute and change under these terms.
|
| 628 |
+
|
| 629 |
+
To do so, attach the following notices to the program. It is safest
|
| 630 |
+
to attach them to the start of each source file to most effectively
|
| 631 |
+
state the exclusion of warranty; and each file should have at least
|
| 632 |
+
the "copyright" line and a pointer to where the full notice is found.
|
| 633 |
+
|
| 634 |
+
<one line to give the program's name and a brief idea of what it does.>
|
| 635 |
+
Copyright (C) <year> <name of author>
|
| 636 |
+
|
| 637 |
+
This program is free software: you can redistribute it and/or modify
|
| 638 |
+
it under the terms of the GNU General Public License as published by
|
| 639 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 640 |
+
(at your option) any later version.
|
| 641 |
+
|
| 642 |
+
This program is distributed in the hope that it will be useful,
|
| 643 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 644 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 645 |
+
GNU General Public License for more details.
|
| 646 |
+
|
| 647 |
+
You should have received a copy of the GNU General Public License
|
| 648 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 649 |
+
|
| 650 |
+
Also add information on how to contact you by electronic and paper mail.
|
| 651 |
+
|
| 652 |
+
If the program does terminal interaction, make it output a short
|
| 653 |
+
notice like this when it starts in an interactive mode:
|
| 654 |
+
|
| 655 |
+
<program> Copyright (C) <year> <name of author>
|
| 656 |
+
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
| 657 |
+
This is free software, and you are welcome to redistribute it
|
| 658 |
+
under certain conditions; type `show c' for details.
|
| 659 |
+
|
| 660 |
+
The hypothetical commands `show w' and `show c' should show the appropriate
|
| 661 |
+
parts of the General Public License. Of course, your program's commands
|
| 662 |
+
might be different; for a GUI interface, you would use an "about box".
|
| 663 |
+
|
| 664 |
+
You should also get your employer (if you work as a programmer) or school,
|
| 665 |
+
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
| 666 |
+
For more information on this, and how to apply and follow the GNU GPL, see
|
| 667 |
+
<https://www.gnu.org/licenses/>.
|
| 668 |
+
|
| 669 |
+
The GNU General Public License does not permit incorporating your program
|
| 670 |
+
into proprietary programs. If your program is a subroutine library, you
|
| 671 |
+
may consider it more useful to permit linking proprietary applications with
|
| 672 |
+
the library. If this is what you want to do, use the GNU Lesser General
|
| 673 |
+
Public License instead of this License. But first, please read
|
| 674 |
+
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
README.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ***`Recruitment Agent`***
|
| 2 |
+
<p align="left">
|
| 3 |
+
<img src="https://img.shields.io/badge/MCP%20Hackathon-Track%202%20%E2%80%94%20Enterprise-blue" />
|
| 4 |
+
<img src="https://img.shields.io/badge/Model%20Context%20Protocol-Enabled-green" />
|
| 5 |
+
<img src="https://img.shields.io/badge/AI%20Agents-Recruitment-orange" />
|
| 6 |
+
<img src="https://img.shields.io/badge/Python-3.10%2B-blue" />
|
| 7 |
+
<img src="https://img.shields.io/badge/LangGraph-Agent%20Orchestration-purple" />
|
| 8 |
+
<img src="https://img.shields.io/badge/Gradio-UI%20Interface-yellow" />
|
| 9 |
+
<img src="https://img.shields.io/badge/HuggingFace-Platform%20%26%20Hackathon-black?logo=huggingface" />
|
| 10 |
+
<img src="https://img.shields.io/badge/OpenAI-LLM%20Powered-0A0A23?logo=openai" />
|
| 11 |
+
<img src="https://img.shields.io/badge/Google%20Cloud-APIs%20%26%20MCP%20Tools-blue?logo=googlecloud" />
|
| 12 |
+
</p>
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
> This project was developed as part of the **[MCP 1st Birthday Hackathon](https://huggingface.co/MCP-1st-Birthday)** — submitted under
|
| 20 |
+
> **Track 2: MCP in Action (Enterprise)**, showcasing a real-world multi-agent application built on top of the Model Context Protocol.
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
<details>
|
| 24 |
+
<summary><strong>📚 Table of Contents</strong> (click to expand)</summary>
|
| 25 |
+
|
| 26 |
+
- [Problem Statement](#problem-statement)
|
| 27 |
+
- [Ethical & Regulatory Considerations](#ethical--regulatory-considerations)
|
| 28 |
+
- [Quick Start: Run Application](#quick-start-run-application)
|
| 29 |
+
- [Services & Ports](#services--ports)
|
| 30 |
+
- [Infrastructure & Secrets](#infrastructure--secrets)
|
| 31 |
+
- [Run Command](#run-command)
|
| 32 |
+
- [Resetting the Environment](#resetting-the-environment)
|
| 33 |
+
- [Application Flow & Entry Points](#application-flow--entry-points)
|
| 34 |
+
- [The Recruitment Lifecycle](#1-the-recruitment-lifecycle)
|
| 35 |
+
- [User Entry Points](#2-user-entry-points)
|
| 36 |
+
- [AI Engineering Principles](#ai-engineering-principles)
|
| 37 |
+
- [Prompt Engineering](#prompt-engineering)
|
| 38 |
+
- [Context Engineering](#context-engineering)
|
| 39 |
+
- [Model & Agent Registry](#model--agent-registry)
|
| 40 |
+
- [Integrated MCP Servers](#integrated-mcp-servers)
|
| 41 |
+
- [License & Acknowledgments](#license--acknowledgments)
|
| 42 |
+
- [Team](#team)
|
| 43 |
+
|
| 44 |
+
</details>
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
## **Problem Statement**
|
| 50 |
+
|
| 51 |
+
Modern recruitment processes remain **slow**, **resource-intensive**, and increasingly **unsustainable** for HR teams amid persistent talent shortages and evolving skill demands. Recent industry reports underscore structural bottlenecks that hinder efficient hiring.
|
| 52 |
+
|
| 53 |
+
High **applicant volumes overwhelm recruiters**, with a *typical job posting attracting hundreds of applications*, many *unqualified*, leading to administrative burdens and rushed evaluations. This results in *only about **5%** of viewers completing applications*, while teams waste time sifting through low-quality submissions. [`1`]
|
| 54 |
+
|
| 55 |
+
Screening and early-stage evaluation consume excessive recruiter time, with **35%** of their efforts dedicated to tasks like interview scheduling alone, exacerbating workload pressures. Talent acquisition leaders report unmanageable demands, with **27%** citing overload as a key issue, up from prior years. [`2`]
|
| 56 |
+
|
| 57 |
+
**Hiring timelines average 44 days across industries**, driven by skills mismatches and manual processes that delay filling critical roles. Globally, **76%** of employers struggle to fill positions due to talent gaps, particularly in tech and healthcare sectors. [`1`, `3`]
|
| 58 |
+
|
| 59 |
+
The financial toll is significant, with **average cost-per-hire reaching $4,700**, fueled by prolonged cycles, high turnover in recruitment teams (projected at **51%** as a top 2025 challenge), and inefficiencies in sourcing. [`1`, `2`]
|
| 60 |
+
|
| 61 |
+
HR professionals **face rising burnout** from these pressures, compounded by competition for diverse talent and the **need for more touchpoints in hiring**, which **45%** of leaders say adds complexity. Skills shortages, cited by **63%** of employers as the primary barrier to growth, further strain teams. [`2`, `4`]
|
| 62 |
+
|
| 63 |
+
These challenges reveal that **traditional manual recruitment fails to scale** in a competitive 2025 landscape. An AI-driven recruitment agent can alleviate bottlenecks by automating screening, accelerating timelines, enhancing consistency, and allowing HR to prioritize strategic decisions over repetitive tasks.
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
### *`References`*
|
| 68 |
+
|
| 69 |
+
1. [HR Cloud — 25 Recruitment Challenges & Solutions in 2025](https://www.hrcloud.com/blog/recruitment-challenges)
|
| 70 |
+
|
| 71 |
+
2. [Select Software Reviews — 100+ Recruitment Statistics Every HR Should Know in 2025](https://www.selectsoftwarereviews.com/blog/recruiting-statistics)
|
| 72 |
+
|
| 73 |
+
3. [Social Talent — The 2025 Hiring Reality Check](https://www.socialtalent.com/leadership/the-2025-hiring-reality-check-data-driven-answers-to-ta-leaders-top-questions)
|
| 74 |
+
|
| 75 |
+
4. [World Economic Forum — The Future of Jobs Report 2025](https://www.weforum.org/publications/the-future-of-jobs-report-2025/digest/)
|
| 76 |
+
|
| 77 |
+
## **Ethical & Regulatory Considerations**
|
| 78 |
+
|
| 79 |
+
This project was developed as an **experimental prototype for a hackathon**, designed to showcase how language-model agents can automate structured workflows. It is **not intended for production deployment** as an autonomous hiring system. Because it touches on the automated assessment of humans, it must be approached with caution and interpreted within the correct ethical and regulatory context.
|
| 80 |
+
|
| 81 |
+
The risks of algorithmic profiling have been widely documented, most notably during the **Cambridge Analytica scandal**, where data from millions of users was harvested and used for psychographic targeting without consent. This episode demonstrated how data-driven models can be leveraged to manipulate individuals when used irresponsibly, and it significantly shaped today’s regulatory landscape. [`5`]
|
| 82 |
+
|
| 83 |
+
Given this history, any system that evaluates or ranks people—particularly in employment—must uphold **strict transparency, human oversight, and narrow scope**. In this prototype, all AI outputs are intended purely as **assistive signals**. The system must **never** be used to autonomously approve, reject, or shortlist candidates.
|
| 84 |
+
|
| 85 |
+
The **EU AI Act** classifies AI systems used for recruitment, CV screening, candidate ranking, promotion decisions, or termination as **High-Risk AI Systems** (Annex III). Such systems are permitted in the EU but must meet stringent requirements, including:
|
| 86 |
+
|
| 87 |
+
- **Human oversight** with the ability to override AI suggestions
|
| 88 |
+
- **Transparency** about the model’s role and limitations
|
| 89 |
+
- **Detailed logging and traceability** of system behavior
|
| 90 |
+
- **Bias monitoring and risk management**
|
| 91 |
+
- **High-quality and relevant training data**
|
| 92 |
+
- **Clear separation** between AI scoring and final human judgment
|
| 93 |
+
|
| 94 |
+
The Act also **prohibits** certain practices in hiring, such as emotion recognition in workplace settings, biometric inference of personality traits, and social-scoring-style ranking systems. [`6`, `7`, `8`]
|
| 95 |
+
|
| 96 |
+
This prototype **does not** conduct emotion recognition, sensitive-trait inference, biometric profiling, or psychographic prediction. It is a technical experiment focused on agent orchestration, workflow automation, and context management—not an end-to-end HR decision engine.
|
| 97 |
+
|
| 98 |
+
### **Human-in-the-Loop by Design**
|
| 99 |
+
To remain aligned with ethical expectations and regulatory requirements, this system must always operate with:
|
| 100 |
+
|
| 101 |
+
- **Human-in-the-Loop (HITL):** Recruiters make all decisions.
|
| 102 |
+
- **Explainability:** Agents produce structured rationales, not black-box judgments.
|
| 103 |
+
- **Data minimization:** Only job-relevant information is processed.
|
| 104 |
+
- **No profiling of protected traits:** No biometric, psychographic, or emotional inference.
|
| 105 |
+
|
| 106 |
+
### **Project Status**
|
| 107 |
+
This project remains a **research and demonstration artifact**, created to explore the technical viability of LLM-powered coordination between agents. It highlights what is technologically possible, but is **not a deployable HR solution** under the EU AI Act. Any real-world implementation would require extensive risk assessment, compliance measures, and human oversight to avoid replicating the harms demonstrated in past profiling scandals.
|
| 108 |
+
|
| 109 |
+
---
|
| 110 |
+
|
| 111 |
+
### *`References`*
|
| 112 |
+
|
| 113 |
+
5. [The Guardian — Cambridge Analytica: A Year On, Lesson in Institutional Failure](https://www.theguardian.com/uk-news/2019/mar/17/cambridge-analytica-year-on-lesson-in-institutional-failure-christopher-wylie)
|
| 114 |
+
|
| 115 |
+
6. [High-level summary of the EU AI Act](https://artificialintelligenceact.eu/high-level-summary/)
|
| 116 |
+
|
| 117 |
+
7. [EU Digital Strategy — Regulatory Framework for AI](https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai)
|
| 118 |
+
|
| 119 |
+
8. [Clifford Chance — What Does the EU AI Act Mean for Employers?](https://www.cliffordchance.com/content/dam/cliffordchance/briefings/2024/08/what-does-the-eu-ai-act-mean-for-employers.pdf)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
## ***`Quick Start: Run Application`***
|
| 124 |
+
To spin up the entire platform including the database, agents, and UI dashboards, we use **Docker Compose**.
|
| 125 |
+
|
| 126 |
+
### ***Services & Ports***
|
| 127 |
+
| Service | Description | Host Port | Container Port |
|
| 128 |
+
|---------|-------------|-----------|----------------|
|
| 129 |
+
| `db` | PostgreSQL 15 database with persistent storage | **5433** | 5432 |
|
| 130 |
+
| `cv_upload_streamlit` | UI for uploading CVs | **8501** | 8501 |
|
| 131 |
+
| `voice_screening_streamlit` | UI for voice screening candidates | **8502** | 8501 |
|
| 132 |
+
| `supervisor_ui` | Main Chat UI for the Supervisor Agent | **8503** | 8501 |
|
| 133 |
+
| `websocket_proxy` | Proxy for OpenAI Realtime API | **8000** | 8000 |
|
| 134 |
+
|
| 135 |
+
### ***Infrastructure & Secrets***
|
| 136 |
+
This project requires Google Cloud credentials for the Gmail and Calendar agents.
|
| 137 |
+
|
| 138 |
+
- **Secrets:** Google tokens and credentials must be present in the `secrets/` directory.
|
| 139 |
+
- **Infrastructure:** You can provision the necessary GCP infrastructure using the code in `terraform/` or the scripts in `scripts/infra/`.
|
| 140 |
+
- **Documentation:** For detailed setup instructions, refer to the [MCP Docs](docs/mcp/).
|
| 141 |
+
|
| 142 |
+
### ***Run Command***
|
| 143 |
+
1. **Configure Environment:**
|
| 144 |
+
Copy the example environment file and fill in your API keys:
|
| 145 |
+
```bash
|
| 146 |
+
cp .env.example .env
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
2. **Start Services:**
|
| 150 |
+
```bash
|
| 151 |
+
docker compose --env-file .env -f docker/docker-compose.yml up --build
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
### 🧹 Resetting the Environment
|
| 155 |
+
If you need a clean slate (e.g., after modifying DB models):
|
| 156 |
+
```bash
|
| 157 |
+
# 1. Stop containers
|
| 158 |
+
docker compose -f docker/docker-compose.yml down
|
| 159 |
+
|
| 160 |
+
# 2. Remove persistent DB volume
|
| 161 |
+
docker volume rm docker_postgres_data
|
| 162 |
+
|
| 163 |
+
# 3. Rebuild & Start
|
| 164 |
+
docker compose --env-file .env -f docker/docker-compose.yml up --build
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
---
|
| 168 |
+
|
| 169 |
+
## ***`Application Flow & Entry Points`***
|
| 170 |
+
|
| 171 |
+
The platform orchestrates a complete recruitment pipeline, interacting with both Candidates and the HR Supervisor.
|
| 172 |
+
|
| 173 |
+
### 1. The Recruitment Lifecycle
|
| 174 |
+
The system tracks candidates through a defined state machine (see `src/state/candidate.py` for the `CandidateStatus` enum).
|
| 175 |
+
|
| 176 |
+
```mermaid
|
| 177 |
+
graph TD
|
| 178 |
+
%% Actors
|
| 179 |
+
Candidate((Candidate))
|
| 180 |
+
HR((HR Supervisor))
|
| 181 |
+
|
| 182 |
+
%% System Components (Nodes)
|
| 183 |
+
CV_UI[CV Portal UI]
|
| 184 |
+
CV_Screen{CV Screening AI}
|
| 185 |
+
Voice_UI[Voice Portal UI]
|
| 186 |
+
Voice_Judge{Voice Judge AI}
|
| 187 |
+
Interview[Person-to-Person Interview]
|
| 188 |
+
Decision{Final Decision}
|
| 189 |
+
|
| 190 |
+
%% Flow & Actions (Edges)
|
| 191 |
+
Candidate -->|1. Uploads CV| CV_UI
|
| 192 |
+
CV_UI -->|2. Triggers Analysis| CV_Screen
|
| 193 |
+
|
| 194 |
+
CV_Screen -->|Pass: Sends Invite| Voice_UI
|
| 195 |
+
CV_Screen -->|Fail: Notify| Rejected((Rejected))
|
| 196 |
+
|
| 197 |
+
Voice_UI -->|3. Conducts Interview| Candidate
|
| 198 |
+
Candidate -->|4. Completes Session| Voice_Judge
|
| 199 |
+
|
| 200 |
+
Voice_Judge -->|Pass: Schedule| Interview
|
| 201 |
+
Voice_Judge -->|Fail: Notify| Rejected
|
| 202 |
+
|
| 203 |
+
Interview -->|5. Feedback| HR
|
| 204 |
+
HR -->|6. Updates Status| Decision
|
| 205 |
+
|
| 206 |
+
Decision -->|Hire| Hired((Hired))
|
| 207 |
+
Decision -->|Reject| Rejected
|
| 208 |
+
|
| 209 |
+
%% Styling
|
| 210 |
+
style CV_UI fill:#e3f2fd,stroke:#1565c0
|
| 211 |
+
style Voice_UI fill:#e3f2fd,stroke:#1565c0
|
| 212 |
+
style CV_Screen fill:#fff3e0,stroke:#ef6c00
|
| 213 |
+
style Voice_Judge fill:#fff3e0,stroke:#ef6c00
|
| 214 |
+
style Interview fill:#e8f5e9,stroke:#2e7d32
|
| 215 |
+
style Decision fill:#f3e5f5,stroke:#7b1fa2
|
| 216 |
+
```
|
| 217 |
+
|
| 218 |
+
### 2. User Entry Points
|
| 219 |
+
|
| 220 |
+
| User | Interface | Port | Description |
|
| 221 |
+
| :--- | :--- | :--- | :--- |
|
| 222 |
+
| **HR Manager** | **Supervisor UI** | `8503` | **The Command Center.** Chat with the Supervisor Agent to manage the pipeline, review candidates, query the DB, and schedule interviews. |
|
| 223 |
+
| **Candidate** | **CV Portal** | `8501` | Public-facing portal for candidates to register and upload their resumes to the system. |
|
| 224 |
+
| **Candidate** | **Voice Portal** | `8502` | AI-conducted voice interview interface. Candidates access this only after passing CV screening and receiving an invite. |
|
| 225 |
+
|
| 226 |
+
---
|
| 227 |
+
|
| 228 |
+
## ***`AI Engineering Principles`***
|
| 229 |
+
|
| 230 |
+
### ***Prompt Engineering***
|
| 231 |
+
|
| 232 |
+
To improve the reliability of complex evaluations (such as CV scoring and Voice Interview judging), we enforce **Chain-of-Thought (CoT)** reasoning within our structured outputs, inspired by [Wei et al. (2022)](https://arxiv.org/abs/2201.11903).
|
| 233 |
+
|
| 234 |
+
By requiring the model to generate a textual explanation *before* assigning numerical scores, we ensure the model "thinks" through the evidence before committing to a decision. This is implemented directly in our Pydantic schemas (e.g., `src/agents/cv_screening/schemas/output_schema.py`), where field order matters:
|
| 235 |
+
|
| 236 |
+
```mermaid
|
| 237 |
+
flowchart LR
|
| 238 |
+
%% Nodes
|
| 239 |
+
Input[Input Data]
|
| 240 |
+
subgraph "Structured Output Schema"
|
| 241 |
+
Feedback["1. Generate Feedback (CoT)"]
|
| 242 |
+
Score["2. Assign Scores"]
|
| 243 |
+
end
|
| 244 |
+
Output[Overall Score]
|
| 245 |
+
|
| 246 |
+
%% Flow
|
| 247 |
+
Input --> Feedback
|
| 248 |
+
Feedback --> Score
|
| 249 |
+
Score --> Output
|
| 250 |
+
|
| 251 |
+
%% Styling
|
| 252 |
+
style Feedback fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
|
| 253 |
+
style Score fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
This simple structural constraint leads to significantly better calibration and reduced hallucination in scoring.
|
| 257 |
+
|
| 258 |
+
### ***Context Engineering***
|
| 259 |
+
|
| 260 |
+
To ensure long-running reliability and precision, this system employs a multi-layered approach to context management. This architecture prevents **"Context Rot"**—a phenomenon where LLM performance degrades as input length increases, as highlighted in [Chroma's research](https://research.trychroma.com/context-rot). By managing context effectively, we ensure agents remain focused and accurate over extended interactions.
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
#### 1. Context Isolation via Delegation
|
| 264 |
+
Instead of a single monolithic agent, tasks are delegated to **specialized sub-agents** (e.g., `cv_screener`, `voice_screener`).
|
| 265 |
+
|
| 266 |
+
- **Delegate (Solid Arrow):** The Supervisor initiates a task, passing only the necessary context to a specific sub-agent.
|
| 267 |
+
- **Report Back (Dotted Arrow):** Once the sub-agent completes its task, it returns a structured summary to the Supervisor, ensuring the main context remains clean.
|
| 268 |
+
|
| 269 |
+
```mermaid
|
| 270 |
+
graph TD
|
| 271 |
+
%% Legend (Top)
|
| 272 |
+
subgraph Legend [Legend]
|
| 273 |
+
direction LR
|
| 274 |
+
KeySup[Supervisor] -->|Delegation| KeyAgent[Sub-Agent]
|
| 275 |
+
KeyAgent -.->|Report Back| KeySup
|
| 276 |
+
end
|
| 277 |
+
|
| 278 |
+
%% Force Legend to be above Supervisor
|
| 279 |
+
Legend ~~~ Supervisor
|
| 280 |
+
|
| 281 |
+
Supervisor[🤖 Supervisor Agent]
|
| 282 |
+
|
| 283 |
+
%% Sub-Agents
|
| 284 |
+
Gmail[📧 Gmail Agent]
|
| 285 |
+
Cal[📅 GCalendar Agent]
|
| 286 |
+
DBExec[💾 DB Executor]
|
| 287 |
+
CV[📄 CV Screener]
|
| 288 |
+
Voice[🎤 Voice Screener]
|
| 289 |
+
|
| 290 |
+
%% Delegation (Outbound)
|
| 291 |
+
Supervisor --> Gmail
|
| 292 |
+
Supervisor --> Cal
|
| 293 |
+
Supervisor --> DBExec
|
| 294 |
+
Supervisor --> CV
|
| 295 |
+
Supervisor --> Voice
|
| 296 |
+
|
| 297 |
+
%% Feedback (Inbound)
|
| 298 |
+
Gmail -.-> Supervisor
|
| 299 |
+
Cal -.-> Supervisor
|
| 300 |
+
DBExec -.-> Supervisor
|
| 301 |
+
CV -.-> Supervisor
|
| 302 |
+
Voice -.-> Supervisor
|
| 303 |
+
|
| 304 |
+
%% Styling
|
| 305 |
+
style Supervisor fill:#e1bee7,stroke:#4a148c,stroke-width:2px
|
| 306 |
+
style Gmail fill:#fff3e0,stroke:#e65100
|
| 307 |
+
style Cal fill:#fff3e0,stroke:#e65100
|
| 308 |
+
style DBExec fill:#fff3e0,stroke:#e65100
|
| 309 |
+
style CV fill:#e3f2fd,stroke:#1565c0
|
| 310 |
+
style Voice fill:#e3f2fd,stroke:#1565c0
|
| 311 |
+
style Legend fill:#f5f5f5,stroke:#9e9e9e,stroke-dasharray: 5 5
|
| 312 |
+
```
|
| 313 |
+
|
| 314 |
+
- **How it works:** Each *sub-agent* operates in its *own isolated context/thread*.
|
| 315 |
+
- **Benefit:** The main Supervisor is not polluted with low-level execution logs. Sub-agents are **stateless** from the Supervisor's perspective—each trigger starts a fresh thread, preventing error accumulation in the workers.
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
#### 2. Context Offloading & Loading (RAG-lite)
|
| 319 |
+
We treat the database not just as storage, but as **offloaded context**.
|
| 320 |
+
- **Offloading:** Candidate data, screening results, and interview states are persisted immediately to a structured SQL/JSON database.
|
| 321 |
+
- **Loading:** The Supervisor does not keep all candidate data in memory. Instead, it utilizes the `db_executor` agent to **retrieve (load)** only the specific data points needed for the current planning step.
|
| 322 |
+
- **Benefit:** Keeps the active context window lean and focused on *reasoning* rather than *storage*.
|
| 323 |
+
|
| 324 |
+
#### 3. Adaptive Context Compaction
|
| 325 |
+
For the **stateful Supervisor** (which manages the long-running user conversation), we implement **Compactive Summarization**.
|
| 326 |
+
- **Mechanism:** As the conversation history exceeds a token threshold, older interactions are summarized into a concise narrative while recent messages are kept verbatim.
|
| 327 |
+
- **Result:** The agent retains "long-term memory" of the conversation arc without hitting context window limits, keeping the Supervisor "forever young."
|
| 328 |
+
|
| 329 |
+
```mermaid
|
| 330 |
+
graph TD
|
| 331 |
+
User[User / API] -->|Long-running Thread| Supervisor
|
| 332 |
+
|
| 333 |
+
subgraph "Stateful & Compacted"
|
| 334 |
+
Supervisor[Supervisor Agent]
|
| 335 |
+
Memory[Context Compaction Module] -.->|Summarizes History| Supervisor
|
| 336 |
+
end
|
| 337 |
+
|
| 338 |
+
subgraph "Stateless & Isolated"
|
| 339 |
+
CV[CV Screener]
|
| 340 |
+
Voice[Voice Screener]
|
| 341 |
+
end
|
| 342 |
+
|
| 343 |
+
subgraph "Context Offloading"
|
| 344 |
+
DB[(Postgres DB)]
|
| 345 |
+
end
|
| 346 |
+
|
| 347 |
+
Supervisor -->|Delegates Task| CV
|
| 348 |
+
Supervisor -->|Delegates Task| Voice
|
| 349 |
+
Supervisor -->|Queries/Updates| DB
|
| 350 |
+
|
| 351 |
+
CV -.->|1. New Thread| CV
|
| 352 |
+
Voice -.->|1. New Thread| Voice
|
| 353 |
+
```
|
| 354 |
+
|
| 355 |
+
## ***`Model & Agent Registry`***
|
| 356 |
+
|
| 357 |
+
A breakdown of the various LLMs, Agents, and Workflows powering the system.
|
| 358 |
+
|
| 359 |
+
- 🤖 **Agent:** Autonomous entity that can use tools, plan multiple steps, and maintain reasoning loops.
|
| 360 |
+
- ⚙️ **Workflow:** Deterministic, fixed sequence of operations (Pipeline). It may use LLMs for specific steps but the flow is hardcoded.
|
| 361 |
+
- 🧠 **Simple LLM:** A direct "one-shot" call to a Language Model for a specific transformation (e.g., summarization, extraction) without tools or loops.
|
| 362 |
+
|
| 363 |
+
| Component | Type | Model | Description | Location |
|
| 364 |
+
| :--- | :--- | :--- | :--- | :--- |
|
| 365 |
+
| **Supervisor Agent** | 🤖 **Agent** | `gpt-4o` | Orchestrates delegation, planning, and context management. | `src/agents/supervisor/supervisor_v2.py` |
|
| 366 |
+
| **Gmail Agent** | 🤖 **Agent** | `gpt-4o` | Autonomous email management via MCP (read/send/label). | `src/agents/gmail/gmail_agent.py` |
|
| 367 |
+
| **GCalendar Agent** | 🤖 **Agent** | `gpt-4o` | Autonomous calendar scheduling via MCP. | `src/agents/gcalendar/gcalendar_agent.py` |
|
| 368 |
+
| **DB Executor** | 🤖 **Agent** | `gpt-4o` | Writes SQL/Python to query the database (CodeAct). | `src/agents/db_executor/db_executor.py` |
|
| 369 |
+
| **CV Screening** | ⚙️ **Workflow** | `gpt-4o` | Deterministic pipeline: Fetch → Read → Evaluate → Save. | `src/agents/cv_screening/cv_screening_workflow.py` |
|
| 370 |
+
| **Voice Judge** | 🧠 **Simple LLM** | `gpt-4o-audio` | Evaluates audio/transcripts for sentiment & confidence. | `src/agents/voice_screening/judge.py` |
|
| 371 |
+
| **Doc Parser** | 🧠 **Simple LLM** | `gpt-4o-mini` | Vision-based PDF-to-Markdown conversion. | `src/doc_parser/pdf_to_markdown.py` |
|
| 372 |
+
| **History Manager** | 🧠 **Simple LLM** | `gpt-4o-mini` | Summarizes conversation history for context compaction. | `src/context_eng/history_manager.py` |
|
| 373 |
+
|
| 374 |
+
### 🔌 ***`Integrated MCP Servers`***
|
| 375 |
+
The system integrates **Model Context Protocol (MCP)** servers to securely and standardizedly connect agents to external tools.
|
| 376 |
+
|
| 377 |
+
| MCP Server | Purpose | Used By |
|
| 378 |
+
| :--- | :--- | :--- |
|
| 379 |
+
| **Gmail MCP** | Provides tools to `list`, `read`, `send`, and `label` emails. | `Gmail Agent` |
|
| 380 |
+
| **Google Calendar MCP** | Provides tools to `list_events`, `create_event`, and `update_event`. | `GCalendar Agent` |
|
| 381 |
+
|
| 382 |
+
> **Note:** Each MCP server runs as a standalone process that exposes a standardized tool interface, which the respective agent consumes dynamically.
|
| 383 |
+
|
| 384 |
+
---
|
| 385 |
+
|
| 386 |
+
## ***`License & Acknowledgments`***
|
| 387 |
+
This project utilizes code from:
|
| 388 |
+
- [gmail-mcp](https://github.com/theposch/gmail-mcp) by **theposch** (GPLv3)
|
| 389 |
+
*Integrated at:* `src/mcp_servers/gmail-mcp/`
|
| 390 |
+
- [calendar-mcp](https://github.com/deciduus/calendar-mcp) by **deciduus** (AGPL-3.0)
|
| 391 |
+
*Integrated at:* `src/mcp_servers/calendar-mcp/`
|
| 392 |
+
|
| 393 |
+
We deeply acknowledge these original works and the great AI and Data Science community that makes such collaboration possible. We distribute our modifications under the compatible license terms.
|
| 394 |
+
|
| 395 |
+
---
|
| 396 |
+
|
| 397 |
+
## 👥 ***`Team`***
|
| 398 |
+
| Member |
|
| 399 |
+
| -------- |
|
| 400 |
+
| [Sebastian Wefers](https://github.com/Ocean-code-1995) |
|
| 401 |
+
| [Dmitri Moscoglo](https://github.com/DimiM99) |
|
| 402 |
+
| [Owen Kaplinsky](https://github.com/owenkaplinsky) |
|
| 403 |
+
| [SrikarMK](https://github.com/Srikarmk) |
|
docker/Dockerfile.candidates_db_init
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# --- Dockerfile.candidates_db_init ---
|
| 2 |
+
FROM python:3.12-slim
|
| 3 |
+
|
| 4 |
+
# Set working directory inside container
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Install system dependencies needed for psycopg2
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
libpq-dev gcc && \
|
| 10 |
+
rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
# Copy requirements file and install dependencies
|
| 13 |
+
|
| 14 |
+
COPY ../requirements/base.txt ./requirements/base.txt
|
| 15 |
+
COPY ../requirements/db.txt ./requirements/db.txt
|
| 16 |
+
RUN pip install --no-cache-dir -r requirements/db.txt
|
| 17 |
+
|
| 18 |
+
# Copy *only* the candidate database module
|
| 19 |
+
COPY src/database/candidates ./src/database/candidates
|
| 20 |
+
|
| 21 |
+
# Default command - use dedicated init script to avoid circular import
|
| 22 |
+
CMD ["python", "-m", "src.database.candidates.init_db"]
|
docker/Dockerfile.cv_upload
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use Python slim base
|
| 2 |
+
FROM python:3.12-slim
|
| 3 |
+
|
| 4 |
+
# Set working directory
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Install system dependencies
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
libpq-dev gcc && \
|
| 10 |
+
rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
# Copy only requirements first (for build caching)
|
| 13 |
+
COPY ../requirements/base.txt ./requirements/base.txt
|
| 14 |
+
COPY ../requirements/db.txt ./requirements/db.txt
|
| 15 |
+
COPY ../requirements/cv_ui.txt ./requirements/cv_ui.txt
|
| 16 |
+
# Install Streamlit + base deps
|
| 17 |
+
RUN pip install --no-cache-dir -r requirements/cv_ui.txt
|
| 18 |
+
|
| 19 |
+
# Copy project source code
|
| 20 |
+
COPY ../src ./src
|
| 21 |
+
|
| 22 |
+
ENV PYTHONPATH=/app
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Expose Streamlit port
|
| 26 |
+
EXPOSE 8501
|
| 27 |
+
|
| 28 |
+
# Default command to run Streamlit app
|
| 29 |
+
CMD ["streamlit", "run", "src/frontend/streamlit/cv_ui/app.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
docker/Dockerfile.supervisor
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.12-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install system dependencies
|
| 6 |
+
RUN apt-get update && apt-get install -y \
|
| 7 |
+
gcc \
|
| 8 |
+
libpq-dev \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Copy requirement files
|
| 12 |
+
COPY requirements/base.txt requirements/base.txt
|
| 13 |
+
COPY requirements/db.txt requirements/db.txt
|
| 14 |
+
COPY requirements/agent.txt requirements/agent.txt
|
| 15 |
+
COPY requirements/supervisor.txt requirements/supervisor.txt
|
| 16 |
+
COPY requirements/mcp_calendar.txt requirements/mcp_calendar.txt
|
| 17 |
+
|
| 18 |
+
# Install dependencies directly (no venv needed in container)
|
| 19 |
+
# **NOTE** uv isntallation is needed for the gmail mcp server to work.
|
| 20 |
+
# >>> it is installed in the base image /usr/local/bin/uv
|
| 21 |
+
RUN pip install --no-cache-dir uv && \
|
| 22 |
+
pip install --no-cache-dir -r requirements/supervisor.txt && \
|
| 23 |
+
pip install --no-cache-dir -r requirements/db.txt && \
|
| 24 |
+
pip install --no-cache-dir -r requirements/agent.txt && \
|
| 25 |
+
pip install --no-cache-dir -r requirements/mcp_calendar.txt
|
| 26 |
+
|
| 27 |
+
# Copy application code
|
| 28 |
+
COPY src/ /app/src/
|
| 29 |
+
COPY secrets/ /app/secrets/
|
| 30 |
+
COPY .env /app/.env
|
| 31 |
+
|
| 32 |
+
# Expose Streamlit port
|
| 33 |
+
EXPOSE 8501
|
| 34 |
+
|
| 35 |
+
# Run Streamlit
|
| 36 |
+
CMD ["streamlit", "run", "src/frontend/streamlit/supervisor_ui/app.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
docker/Dockerfile.supervisor_api
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.12-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install system dependencies
|
| 6 |
+
RUN apt-get update && apt-get install -y \
|
| 7 |
+
gcc \
|
| 8 |
+
libpq-dev \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Copy requirement files
|
| 12 |
+
COPY requirements/base.txt requirements/base.txt
|
| 13 |
+
COPY requirements/db.txt requirements/db.txt
|
| 14 |
+
COPY requirements/agent.txt requirements/agent.txt
|
| 15 |
+
COPY requirements/supervisor.txt requirements/supervisor.txt
|
| 16 |
+
COPY requirements/api.txt requirements/api.txt
|
| 17 |
+
COPY requirements/cv_ui.txt requirements/cv_ui.txt
|
| 18 |
+
COPY requirements/mcp_calendar.txt requirements/mcp_calendar.txt
|
| 19 |
+
COPY requirements/mcp_gmail.txt requirements/mcp_gmail.txt
|
| 20 |
+
|
| 21 |
+
# Install dependencies directly (no venv needed in container)
|
| 22 |
+
# **NOTE** uv installation is needed for the gmail mcp server to work.
|
| 23 |
+
# >>> it is installed in the base image /usr/local/bin/uv
|
| 24 |
+
RUN pip install --no-cache-dir uv && \
|
| 25 |
+
pip install --no-cache-dir -r requirements/supervisor.txt && \
|
| 26 |
+
pip install --no-cache-dir -r requirements/db.txt && \
|
| 27 |
+
pip install --no-cache-dir -r requirements/agent.txt && \
|
| 28 |
+
pip install --no-cache-dir -r requirements/api.txt && \
|
| 29 |
+
pip install --no-cache-dir -r requirements/cv_ui.txt && \
|
| 30 |
+
pip install --no-cache-dir -r requirements/mcp_calendar.txt && \
|
| 31 |
+
pip install --no-cache-dir -r requirements/mcp_gmail.txt
|
| 32 |
+
|
| 33 |
+
# Copy application code
|
| 34 |
+
COPY src/ /app/src/
|
| 35 |
+
COPY secrets/ /app/secrets/
|
| 36 |
+
COPY .env /app/.env
|
| 37 |
+
|
| 38 |
+
# Expose API port
|
| 39 |
+
EXPOSE 8080
|
| 40 |
+
|
| 41 |
+
# Run FastAPI with uvicorn
|
| 42 |
+
CMD ["uvicorn", "src.api.app:app", "--host", "0.0.0.0", "--port", "8080"]
|
| 43 |
+
|
docker/Dockerfile.voice_proxy
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use Python slim base
|
| 2 |
+
FROM python:3.12-slim
|
| 3 |
+
|
| 4 |
+
# Set working directory
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Install system dependencies
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
libpq-dev gcc && \
|
| 10 |
+
rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
# Copy only requirements first (for build caching)
|
| 13 |
+
COPY ../requirements/base.txt ./requirements/base.txt
|
| 14 |
+
COPY ../requirements/db.txt ./requirements/db.txt
|
| 15 |
+
COPY ../requirements/voice_proxy.txt ./requirements/voice_proxy.txt
|
| 16 |
+
# Install Streamlit + base deps
|
| 17 |
+
RUN pip install --no-cache-dir -r requirements/voice_proxy.txt
|
| 18 |
+
|
| 19 |
+
# Copy project source code
|
| 20 |
+
COPY ../src ./src
|
| 21 |
+
|
| 22 |
+
ENV PYTHONPATH=/app
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Expose FastAPI port
|
| 26 |
+
EXPOSE 8000
|
| 27 |
+
|
| 28 |
+
# Default command to run FastAPI proxy
|
| 29 |
+
CMD ["python", "-m", "uvicorn", "src.frontend.streamlit.voice_screening_ui.proxy:app", "--host", "0.0.0.0", "--port", "8000"]
|
docker/Dockerfile.voice_screening
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use Python slim base
|
| 2 |
+
FROM python:3.12-slim
|
| 3 |
+
|
| 4 |
+
# Set working directory
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Install system dependencies
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
libpq-dev gcc && \
|
| 10 |
+
rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
# Copy only requirements first (for build caching)
|
| 13 |
+
COPY ../requirements/base.txt ./requirements/base.txt
|
| 14 |
+
COPY ../requirements/voice_screening_ui.txt ./requirements/voice_screening_ui.txt
|
| 15 |
+
# Install Streamlit + base deps
|
| 16 |
+
RUN pip install --no-cache-dir -r requirements/voice_screening_ui.txt
|
| 17 |
+
|
| 18 |
+
# Copy project source code
|
| 19 |
+
COPY ../src ./src
|
| 20 |
+
|
| 21 |
+
ENV PYTHONPATH=/app
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Expose Streamlit port
|
| 25 |
+
EXPOSE 8501
|
| 26 |
+
|
| 27 |
+
# Default command to run Streamlit app
|
| 28 |
+
CMD ["streamlit", "run", "src/frontend/streamlit/voice_screening_ui/app.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
docker/docker-compose.yml
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
|
| 2 |
+
# Run as follows from root:
|
| 3 |
+
# >>> docker compose --env-file .env -f docker/docker-compose.yml up --build
|
| 4 |
+
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
|
| 5 |
+
|
| 6 |
+
services:
|
| 7 |
+
# --- Database Service ---
|
| 8 |
+
# Runs a PostgreSQL 15 instance with persistent storage.
|
| 9 |
+
db:
|
| 10 |
+
image: postgres:15
|
| 11 |
+
container_name: agentic_hr_db
|
| 12 |
+
restart: always
|
| 13 |
+
ports:
|
| 14 |
+
- "5433:5432"
|
| 15 |
+
volumes:
|
| 16 |
+
- postgres_data:/var/lib/postgresql/data
|
| 17 |
+
healthcheck:
|
| 18 |
+
test: ["CMD-SHELL", "pg_isready -U agentic_user -d agentic_hr"]
|
| 19 |
+
interval: 3s
|
| 20 |
+
timeout: 3s
|
| 21 |
+
retries: 5
|
| 22 |
+
environment:
|
| 23 |
+
POSTGRES_HOST: ${POSTGRES_HOST}
|
| 24 |
+
POSTGRES_PORT: ${POSTGRES_PORT}
|
| 25 |
+
POSTGRES_USER: ${POSTGRES_USER}
|
| 26 |
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
| 27 |
+
POSTGRES_DB: ${POSTGRES_DB}
|
| 28 |
+
networks:
|
| 29 |
+
- hrnet
|
| 30 |
+
|
| 31 |
+
candidates_db_init:
|
| 32 |
+
# --- Application Service ---
|
| 33 |
+
# Runs your Python backend inside Docker.
|
| 34 |
+
# Initializes the database or starts the API (depending on command).
|
| 35 |
+
container_name: candidates_db_init
|
| 36 |
+
build:
|
| 37 |
+
context: .. # build from the project root
|
| 38 |
+
dockerfile: docker/Dockerfile.candidates_db_init
|
| 39 |
+
depends_on:
|
| 40 |
+
db:
|
| 41 |
+
condition: service_healthy
|
| 42 |
+
environment:
|
| 43 |
+
POSTGRES_HOST: ${POSTGRES_HOST}
|
| 44 |
+
POSTGRES_PORT: ${POSTGRES_PORT}
|
| 45 |
+
POSTGRES_USER: ${POSTGRES_USER}
|
| 46 |
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
| 47 |
+
POSTGRES_DB: ${POSTGRES_DB}
|
| 48 |
+
# command: ["python", "-m", "src.database.candidates.init_db"]
|
| 49 |
+
|
| 50 |
+
volumes:
|
| 51 |
+
# --- Local code mount (for development only) ---
|
| 52 |
+
# Mounts your entire project source from the host (../)
|
| 53 |
+
# into the container at /app.
|
| 54 |
+
# ✅ Enables live code changes without rebuilding the image.
|
| 55 |
+
# ⚠️ Do NOT use in production – overrides the built image code.
|
| 56 |
+
- ../:/app # optional: live reload for local dev
|
| 57 |
+
|
| 58 |
+
networks:
|
| 59 |
+
- hrnet
|
| 60 |
+
|
| 61 |
+
# --- CV Upload ---
|
| 62 |
+
cv_upload_streamlit:
|
| 63 |
+
container_name: cv_upload_streamlit
|
| 64 |
+
build:
|
| 65 |
+
context: ..
|
| 66 |
+
dockerfile: docker/Dockerfile.cv_upload
|
| 67 |
+
ports:
|
| 68 |
+
- "8501:8501"
|
| 69 |
+
depends_on:
|
| 70 |
+
- db
|
| 71 |
+
- supervisor_api
|
| 72 |
+
environment:
|
| 73 |
+
# Database connection
|
| 74 |
+
POSTGRES_HOST: ${POSTGRES_HOST}
|
| 75 |
+
POSTGRES_PORT: ${POSTGRES_PORT}
|
| 76 |
+
POSTGRES_USER: ${POSTGRES_USER}
|
| 77 |
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
| 78 |
+
POSTGRES_DB: ${POSTGRES_DB}
|
| 79 |
+
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
| 80 |
+
CV_UPLOAD_PATH: /app/src/database/cvs/uploads
|
| 81 |
+
# App specific
|
| 82 |
+
CV_UPLOAD_API_URL: http://supervisor_api:8080/api/v1/cv
|
| 83 |
+
PYTHONPATH: /app
|
| 84 |
+
volumes:
|
| 85 |
+
# Mount local code for live updates
|
| 86 |
+
- ../:/app
|
| 87 |
+
# Shared volume for CV uploads (persistent)
|
| 88 |
+
- ../src/database/cvs:/app/src/database/cvs
|
| 89 |
+
command:
|
| 90 |
+
[
|
| 91 |
+
"streamlit",
|
| 92 |
+
"run",
|
| 93 |
+
"src/frontend/streamlit/cv_ui/app.py",
|
| 94 |
+
"--server.port=8501",
|
| 95 |
+
"--server.address=0.0.0.0",
|
| 96 |
+
]
|
| 97 |
+
networks:
|
| 98 |
+
- hrnet
|
| 99 |
+
|
| 100 |
+
# --- WebSocket Proxy for OpenAI Realtime API ---
|
| 101 |
+
websocket_proxy:
|
| 102 |
+
container_name: websocket_proxy
|
| 103 |
+
build:
|
| 104 |
+
context: ..
|
| 105 |
+
dockerfile: docker/Dockerfile.voice_proxy
|
| 106 |
+
ports:
|
| 107 |
+
- "8000:8000"
|
| 108 |
+
depends_on:
|
| 109 |
+
- db
|
| 110 |
+
- candidates_db_init
|
| 111 |
+
environment:
|
| 112 |
+
PYTHONPATH: /app
|
| 113 |
+
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
| 114 |
+
BACKEND_API_URL: http://supervisor_api:8080
|
| 115 |
+
volumes:
|
| 116 |
+
# Mount local code for live updates
|
| 117 |
+
- ../:/app
|
| 118 |
+
command:
|
| 119 |
+
[
|
| 120 |
+
"python",
|
| 121 |
+
"-m",
|
| 122 |
+
"uvicorn",
|
| 123 |
+
"src.frontend.streamlit.voice_screening_ui.proxy:app",
|
| 124 |
+
"--host",
|
| 125 |
+
"0.0.0.0",
|
| 126 |
+
"--port",
|
| 127 |
+
"8000",
|
| 128 |
+
]
|
| 129 |
+
networks:
|
| 130 |
+
- hrnet
|
| 131 |
+
|
| 132 |
+
# --- Voice Screening UI ---
|
| 133 |
+
voice_screening_ui:
|
| 134 |
+
container_name: voice_screening_ui
|
| 135 |
+
build:
|
| 136 |
+
context: ..
|
| 137 |
+
dockerfile: docker/Dockerfile.voice_screening
|
| 138 |
+
ports:
|
| 139 |
+
- "8502:8501" # Map host port 8502 to container port 8501
|
| 140 |
+
depends_on:
|
| 141 |
+
- db
|
| 142 |
+
- websocket_proxy
|
| 143 |
+
environment:
|
| 144 |
+
DATABASE_URL: postgresql://agentic_user:password123@db:5432/agentic_hr
|
| 145 |
+
PYTHONPATH: /app
|
| 146 |
+
WEBSOCKET_PROXY_URL: ws://websocket_proxy:8000/ws/realtime
|
| 147 |
+
BACKEND_API_URL: http://supervisor_api:8080
|
| 148 |
+
volumes:
|
| 149 |
+
# Mount local code for live updates
|
| 150 |
+
- ../:/app
|
| 151 |
+
command:
|
| 152 |
+
[
|
| 153 |
+
"streamlit",
|
| 154 |
+
"run",
|
| 155 |
+
"src/frontend/streamlit/voice_screening_ui/app.py",
|
| 156 |
+
"--server.port=8501",
|
| 157 |
+
"--server.address=0.0.0.0",
|
| 158 |
+
]
|
| 159 |
+
networks:
|
| 160 |
+
- hrnet
|
| 161 |
+
|
| 162 |
+
# --- Supervisor Agent API ---
|
| 163 |
+
supervisor_api:
|
| 164 |
+
container_name: supervisor_api
|
| 165 |
+
build:
|
| 166 |
+
context: ..
|
| 167 |
+
dockerfile: docker/Dockerfile.supervisor_api
|
| 168 |
+
ports:
|
| 169 |
+
- "8080:8080" # Map host port 8080 to container port 8080
|
| 170 |
+
depends_on:
|
| 171 |
+
- db
|
| 172 |
+
environment:
|
| 173 |
+
# We set POSTGRES_HOST to 'db' so the agent connects to the container internal network
|
| 174 |
+
POSTGRES_HOST: ${POSTGRES_HOST}
|
| 175 |
+
POSTGRES_PORT: ${POSTGRES_PORT}
|
| 176 |
+
POSTGRES_USER: ${POSTGRES_USER}
|
| 177 |
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
| 178 |
+
POSTGRES_DB: ${POSTGRES_DB}
|
| 179 |
+
PYTHONPATH: /app
|
| 180 |
+
PROMPTLAYER_API_KEY: ${PROMPTLAYER_API_KEY}
|
| 181 |
+
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
| 182 |
+
WEBSOCKET_PROXY_URL: ws://websocket_proxy:8000/ws/realtime
|
| 183 |
+
volumes:
|
| 184 |
+
# Mount local code for live updates
|
| 185 |
+
- ../:/app
|
| 186 |
+
command:
|
| 187 |
+
[
|
| 188 |
+
"uvicorn",
|
| 189 |
+
"src.api.app:app",
|
| 190 |
+
"--host",
|
| 191 |
+
"0.0.0.0",
|
| 192 |
+
"--port",
|
| 193 |
+
"8080",
|
| 194 |
+
"--reload",
|
| 195 |
+
]
|
| 196 |
+
networks:
|
| 197 |
+
- hrnet
|
| 198 |
+
|
| 199 |
+
# --- Supervisor Agent UI ---
|
| 200 |
+
supervisor_ui:
|
| 201 |
+
container_name: supervisor_ui
|
| 202 |
+
build:
|
| 203 |
+
context: ..
|
| 204 |
+
dockerfile: docker/Dockerfile.supervisor
|
| 205 |
+
ports:
|
| 206 |
+
- "8503:8501" # Map host port 8503 to container port 8501
|
| 207 |
+
depends_on:
|
| 208 |
+
- db
|
| 209 |
+
- supervisor_api
|
| 210 |
+
environment:
|
| 211 |
+
# We set POSTGRES_HOST to 'db' so the agent connects to the container internal network
|
| 212 |
+
PYTHONPATH: /app
|
| 213 |
+
# API URL for the Streamlit UI to connect to
|
| 214 |
+
SUPERVISOR_API_URL: http://supervisor_api:8080/api/v1/supervisor
|
| 215 |
+
volumes:
|
| 216 |
+
# Mount local code for live updates
|
| 217 |
+
- ../:/app
|
| 218 |
+
command:
|
| 219 |
+
[
|
| 220 |
+
"streamlit",
|
| 221 |
+
"run",
|
| 222 |
+
"src/frontend/streamlit/supervisor_ui/app.py",
|
| 223 |
+
"--server.port=8501",
|
| 224 |
+
"--server.address=0.0.0.0",
|
| 225 |
+
]
|
| 226 |
+
networks:
|
| 227 |
+
- hrnet
|
| 228 |
+
|
| 229 |
+
volumes:
|
| 230 |
+
postgres_data:
|
| 231 |
+
cvs_data:
|
| 232 |
+
driver: local
|
| 233 |
+
|
| 234 |
+
networks:
|
| 235 |
+
hrnet:
|
| 236 |
+
driver: bridge
|
docker/info.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 🐳 Docker Services
|
| 2 |
+
|
| 3 |
+
### Services and Ports
|
| 4 |
+
|
| 5 |
+
| Service | Description | Host Port | Container Port |
|
| 6 |
+
|---------|-------------|-----------|----------------|
|
| 7 |
+
| `db` | PostgreSQL 15 database with persistent storage | 5433 | 5432 |
|
| 8 |
+
| `candidates_db_init` | Python backend container — initializes DB schema | N/A | N/A |
|
| 9 |
+
| `cv_upload_streamlit` | Streamlit app for CV uploads | 8501 | 8501 |
|
| 10 |
+
| `websocket_proxy` | WebSocket proxy for OpenAI Realtime API | 8000 | 8000 |
|
| 11 |
+
| `voice_screening_ui` | Streamlit app for voice screening | 8502 | 8501 |
|
| 12 |
+
| `supervisor_ui` | Streamlit app for Supervisor Agent | 8503 | 8501 |
|
| 13 |
+
| `supervisor_api` | FastAPI backend for Supervisor Agent | 8080 | 8080 |
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
### Run Command
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
docker compose --env-file .env -f docker/docker-compose.yml up --build
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
### Resetting the Environment
|
| 26 |
+
|
| 27 |
+
When making structural changes to the database (e.g., modifying models, updating Enums) or when you simply want a clean slate for testing, the persistent Docker volumes may cause conflicts with new code.
|
| 28 |
+
|
| 29 |
+
To completely reset the environment and database:
|
| 30 |
+
|
| 31 |
+
```bash
|
| 32 |
+
# 1. Stop running containers
|
| 33 |
+
docker compose -f docker/docker-compose.yml down
|
| 34 |
+
|
| 35 |
+
# 2. Remove the persistent database volume
|
| 36 |
+
docker volume rm docker_postgres_data
|
| 37 |
+
|
| 38 |
+
# 3. Rebuild and start fresh
|
| 39 |
+
docker compose --env-file .env -f docker/docker-compose.yml up --build
|
| 40 |
+
```
|
docs/agents/agent_ochestrator.md
ADDED
|
File without changes
|
docs/agents/cv_screening.md
ADDED
|
File without changes
|
docs/agents/google_mcp_agent.md
ADDED
|
File without changes
|
docs/agents/judging_agent.md
ADDED
|
File without changes
|
docs/agents/supervisor/mvps/mvp1/mvp_v1.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Owens
|
docs/agents/supervisor/mvps/mvp2/mvp_v2.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ***MVP version #2***
|
| 2 |
+
---
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
## HR-Triggered Autonomous Workflow Concept
|
| 6 |
+
|
| 7 |
+
The system is designed to operate **autonomously** while still allowing HR to initiate workflows and request status insights.
|
| 8 |
+
This ensures maximum automation without losing control or clarity in the process.
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
### **HR Interaction Trigger**
|
| 13 |
+
|
| 14 |
+
When HR opens the UI, they can interact with the supervisor agent by asking questions such as:
|
| 15 |
+
|
| 16 |
+
> **“Hey recruitment agent, what is the current status quo? Any new applicants? How many have passed CV screening?”**
|
| 17 |
+
|
| 18 |
+
The supervisor agent then:
|
| 19 |
+
|
| 20 |
+
1. Queries the database using predefined tools
|
| 21 |
+
2. Generates a clear, human-friendly status report
|
| 22 |
+
3. Waits for HR instructions on how to proceed
|
| 23 |
+
|
| 24 |
+
HR can then issue high-level commands like:
|
| 25 |
+
|
| 26 |
+
- **“Process all new applicants.”**
|
| 27 |
+
- **“Do not process new applicants further — notify them instead.”**
|
| 28 |
+
- **“Continue processing only applicants who already passed screening.”**
|
| 29 |
+
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
### **Supervisor Executes Fully Autonomous Actions**
|
| 33 |
+
|
| 34 |
+
Once HR gives the high-level command, the supervisor performs all actions autonomously:
|
| 35 |
+
|
| 36 |
+
- **Process new applicants**
|
| 37 |
+
- Parse CVs
|
| 38 |
+
- Run CV screening
|
| 39 |
+
- Update DB
|
| 40 |
+
- Notify or proceed depending on results
|
| 41 |
+
|
| 42 |
+
- **Process screened applicants further**
|
| 43 |
+
- Notify candidate
|
| 44 |
+
- Request available time slots
|
| 45 |
+
- Match HR availability
|
| 46 |
+
- Schedule interview
|
| 47 |
+
- Send confirmation emails
|
| 48 |
+
|
| 49 |
+
---
|
| 50 |
+
|
| 51 |
+
### **Concurrency and Isolation**
|
| 52 |
+
|
| 53 |
+
To avoid mixing contexts across candidates:
|
| 54 |
+
|
| 55 |
+
- **Only one supervisor agent instance runs at a time**
|
| 56 |
+
- Supervisor processes candidates **sequentially**
|
| 57 |
+
- Each candidate is handled **individually and deterministically**
|
| 58 |
+
|
| 59 |
+
This avoids:
|
| 60 |
+
- Context bleed
|
| 61 |
+
- Duplicate actions
|
| 62 |
+
- Race conditions
|
| 63 |
+
- Mixed reasoning across candidates
|
| 64 |
+
|
| 65 |
+
---
|
| 66 |
+
|
| 67 |
+
### **Per-Candidate Deterministic State Machine**
|
| 68 |
+
|
| 69 |
+
Each candidate has a small state object:
|
| 70 |
+
|
| 71 |
+
```json
|
| 72 |
+
{
|
| 73 |
+
"candidate_id": 123,
|
| 74 |
+
"state": "cv_uploaded",
|
| 75 |
+
"checklist_path": "users/123/checklist.md"
|
| 76 |
+
}
|
| 77 |
+
```
|
| 78 |
+
This keeps the workflow predictable, restartable, and isolated.
|
| 79 |
+
|
| 80 |
+
---
|
| 81 |
+
|
| 82 |
+
### ***Per-Candidate Checklist File***
|
| 83 |
+
Each candidate has a personal Markdown checklist:
|
| 84 |
+
```text
|
| 85 |
+
# Candidate Checklist — ID 123
|
| 86 |
+
|
| 87 |
+
- [x] CV uploaded
|
| 88 |
+
- [x] CV parsed and stored
|
| 89 |
+
- [x] CV screening started
|
| 90 |
+
- [x] CV screening completed
|
| 91 |
+
- [ ] Screening results notified to candidate
|
| 92 |
+
- [ ] Asked candidate for available time slots
|
| 93 |
+
- [ ] Received candidate availability
|
| 94 |
+
- [ ] Checked HR availability
|
| 95 |
+
- [ ] Scheduled interview
|
| 96 |
+
- [ ] Final confirmation email sent
|
| 97 |
+
```
|
| 98 |
+
The supervisor uses this checklist to determine the next atomic action.
|
| 99 |
+
It loads only this candidate’s context, performs exactly one update, writes back, and moves on.
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
## **Hybrid Progress Tracking — DB Status + Checklist**
|
| 103 |
+
|
| 104 |
+
The HR agent maintains two synchronized layers of workflow state:
|
| 105 |
+
|
| 106 |
+
- **Database `status` field:**
|
| 107 |
+
Captures the **coarse-grained milestone** in the candidate’s lifecycle
|
| 108 |
+
(e.g., `applied`, `cv_screened`, `interview_scheduled`, `decision_made`).
|
| 109 |
+
→ This is the **authoritative system state** used for HR dashboards, analytics, and reporting.
|
| 110 |
+
|
| 111 |
+
- **Per-candidate Markdown checklist:**
|
| 112 |
+
Tracks **fine-grained atomic actions** that occur within each milestone
|
| 113 |
+
(e.g., CV parsed, CV screened, email sent, candidate replied).
|
| 114 |
+
→ This serves as the **agent’s operational log**, enabling deterministic reasoning, auditing, and safe restarts.
|
| 115 |
+
|
| 116 |
+
---
|
| 117 |
+
|
| 118 |
+
### **Checklist and Milestone Boundaries**
|
| 119 |
+
|
| 120 |
+
The **checklist** is composed of multiple **substeps**, each representing one small, deterministic action.
|
| 121 |
+
When all substeps belonging to a stage are completed, the system reaches a **milestone boundary**.
|
| 122 |
+
That boundary marks a safe point to update the candidate’s `status` field in the database.
|
| 123 |
+
|
| 124 |
+
| Milestone (`status` in DB) | Meaning | Checklist Substeps Leading to Boundary |
|
| 125 |
+
|-----------------------------|----------|---------------------------------------|
|
| 126 |
+
| `applied` | Candidate record created | `[x] CV uploaded`, `[x] CV parsed` |
|
| 127 |
+
| `cv_screened` | Screening phase finished | `[x] Screening started`, `[x] Screening completed`, `[x] Result stored` |
|
| 128 |
+
| `interview_scheduled` | Interview arranged | `[x] Candidate notified`, `[x] Availability received`, `[x] Interview scheduled` |
|
| 129 |
+
| `decision_made` | Final decision delivered | `[x] Interview completed`, `[x] Decision logged`, `[x] Notification email sent` |
|
| 130 |
+
|
| 131 |
+
---
|
| 132 |
+
|
| 133 |
+
### **Sync Rule**
|
| 134 |
+
|
| 135 |
+
1. After **each atomic substep**, the supervisor updates the checklist file.
|
| 136 |
+
2. When a **milestone boundary** is reached (all substeps for a phase checked off),
|
| 137 |
+
the supervisor updates the corresponding `status` field in the database.
|
| 138 |
+
3. The checklist remains the **fine-grained operational truth**,
|
| 139 |
+
while the database holds the **coarse-grained canonical truth**.
|
| 140 |
+
|
| 141 |
+
---
|
| 142 |
+
|
| 143 |
+
### **Summary**
|
| 144 |
+
|
| 145 |
+
- **Checklist = micro-level progress tracker** (agent reasoning + recovery)
|
| 146 |
+
- **Milestone boundaries = transition triggers** (define when to sync with DB)
|
| 147 |
+
- **Database `status` = macro-level lifecycle state** (system-wide reference)
|
| 148 |
+
|
| 149 |
+
This hybrid approach combines **LLM-friendly transparency** with **system-level consistency**, ensuring the agent can reason, recover, and scale safely.
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
---
|
| 153 |
+
|
| 154 |
+
### ***Result***
|
| 155 |
+
This approach provides:
|
| 156 |
+
- High autonomy
|
| 157 |
+
- Strong safety boundaries
|
| 158 |
+
- No context mixing
|
| 159 |
+
- Clear state tracking
|
| 160 |
+
- Reliable execution
|
| 161 |
+
- HR keeps high-level control
|
| 162 |
+
- LLM handles reasoning, routing, and next steps autonomously
|
| 163 |
+
This structure is scalable, maintainable, and production-friendly while still pushing agent autonomy very far.
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
## ⚡ ***Handling Everything Concurrently — The Async Supervisor Layer***
|
| 167 |
+
---
|
| 168 |
+
`The system must support concurrent processing of multiple candidate groups, each representing a different stage in the application pipeline (e.g., CV screening, voice screening, decision). Within each group, it should be able to process batches of candidates simultaneously while preserving per-candidate isolation and state consistency.`
|
| 169 |
+
|
| 170 |
+
### **Thread-Based Per-Candidate Isolation For the Rescue**
|
| 171 |
+
|
| 172 |
+
To ensure deterministic, fault-tolerant, and concurrent execution, the system leverages **LangGraph thread IDs** for per-candidate isolation:
|
| 173 |
+
|
| 174 |
+
1. **Supervisor Delegation**
|
| 175 |
+
The Supervisor Agent queries all candidates, groups them by their current `status` (e.g., CV screening, voice screening, decision), and passes the **list of candidate IDs** to the appropriate subagent tool.
|
| 176 |
+
Each subagent handles its own data loading, ensuring the Supervisor remains lightweight and purely orchestration-focused.
|
| 177 |
+
|
| 178 |
+
2. **Subagent Execution (Thread-per-Candidate)**
|
| 179 |
+
Inside each subagent (e.g., `screen_cv`), the system iterates over all received candidate IDs.
|
| 180 |
+
For each candidate:
|
| 181 |
+
- The **candidate ID serves as the `thread_id`**, providing a unique persistent context in LangGraph.
|
| 182 |
+
- The subagent loads candidate data from the database (CV path, JD path, etc.).
|
| 183 |
+
- The CV or voice screening logic runs **within that thread’s context**.
|
| 184 |
+
- On completion, the results are written back to the database, and the per-candidate checklist and state are updated.
|
| 185 |
+
|
| 186 |
+
3. **Parallel and Safe Processing**
|
| 187 |
+
Subagents can process multiple candidates concurrently by spawning asynchronous executions per `thread_id`.
|
| 188 |
+
Each candidate’s context remains isolated, preventing race conditions or context mixing.
|
| 189 |
+
|
| 190 |
+
**Result:**
|
| 191 |
+
- Supervisor coordinates and dispatches candidate groups
|
| 192 |
+
- Subagents handle per-candidate logic using thread-based persistence
|
| 193 |
+
- Each candidate’s run is self-contained, recoverable, and writes its final results back to the database
|
| 194 |
+
|
| 195 |
+
> **Note:**
|
| 196 |
+
> During a “Process All” operation, the Supervisor Agent executes **multiple reasoning loops**, invoking each subagent tool in sequence (e.g., `screen_cv`, `voice_screening`, `schedule_hr_interview`).
|
| 197 |
+
> After each tool call, it observes the result, reasons about the next step, and continues until all candidate groups are processed.
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
Latest chat: https://chatgpt.com/share/6920d318-3f64-8012-8fca-b17316093131
|
| 202 |
+
|
| 203 |
+
---
|
| 204 |
+
|
| 205 |
+
> below mst be adapted based on section above:
|
| 206 |
+
|
| 207 |
+
...
|
| 208 |
+
```mermaid
|
| 209 |
+
flowchart TD
|
| 210 |
+
|
| 211 |
+
HR_UI[UI: HR opens dashboard<br/>and requests candidate status]
|
| 212 |
+
--> REPORT[System returns report<br/>showing new and screened candidates]
|
| 213 |
+
|
| 214 |
+
REPORT --> PARALLEL[Async Supervisor<br/>launches concurrent group tasks]
|
| 215 |
+
|
| 216 |
+
%% --- New candidate path ---
|
| 217 |
+
PARALLEL --> NEW_FLOW[Process new candidates<br/>CV screening pipeline]
|
| 218 |
+
NEW_FLOW --> A[Delegate screening to subagent]
|
| 219 |
+
A --> B[Subagent screens CV]
|
| 220 |
+
B --> C[Write screening results to DB]
|
| 221 |
+
C --> D[Supervisor receives results]
|
| 222 |
+
D --> E{Did candidate pass screening?}
|
| 223 |
+
E -- No --> REJECT[Notify candidate and HR<br/>application rejected]
|
| 224 |
+
E -- Yes --> PASSED[Notify candidate and HR<br/>passed screening]
|
| 225 |
+
|
| 226 |
+
%% --- Screened candidate path ---
|
| 227 |
+
PARALLEL --> SCREENED_FLOW[Process screened candidates<br/>interview scheduling pipeline]
|
| 228 |
+
SCREENED_FLOW --> I[Request candidate time slots]
|
| 229 |
+
I --> J[Check HR calendar availability]
|
| 230 |
+
J --> K[Schedule interview]
|
| 231 |
+
K --> L[Notify HR and candidate<br/>interview confirmed]
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
```
|
| 236 |
+
|
| 237 |
+
To support concurrent processing across groups and candidates, the supervisor now operates as an asynchronous orchestrator.
|
| 238 |
+
It remains a single agent context — responsible for reasoning, reporting, and orchestration — but leverages asyncio to execute multiple workflows concurrently.
|
| 239 |
+
This allows the system to:
|
| 240 |
+
- Process multiple groups (e.g., new vs. screened candidates) in parallel
|
| 241 |
+
- Process multiple candidates per group concurrently
|
| 242 |
+
- Maintain isolation and determinism per candidate through separate state/checklist files
|
| 243 |
+
|
| 244 |
+
---
|
| 245 |
+
|
| 246 |
+
Conceptual Overview
|
| 247 |
+
1. HR issues a high-level command (e.g., “Process all candidates”).
|
| 248 |
+
2. The supervisor queries the database and identifies candidate groups.
|
| 249 |
+
3. It launches async tasks for each group simultaneously.
|
| 250 |
+
4. Within each group, candidates are processed concurrently — each running the deterministic checklist logic described above.
|
| 251 |
+
5. The supervisor awaits completion of all group tasks and reports progress and results.
|
| 252 |
+
|
| 253 |
+
This preserves:
|
| 254 |
+
- ✅ Single supervisor reasoning context
|
| 255 |
+
- ✅ Concurrent group + per-candidate execution
|
| 256 |
+
- ✅ Isolated per-candidate state and file I/O
|
| 257 |
+
- ✅ High throughput without context bleed
|
| 258 |
+
|
| 259 |
+
---
|
| 260 |
+
|
| 261 |
+
***Conceptual Async Code Example***
|
| 262 |
+
|
| 263 |
+
```python
|
| 264 |
+
import asyncio
|
| 265 |
+
from typing import List
|
| 266 |
+
|
| 267 |
+
# --- Candidate-level deterministic flow ---
|
| 268 |
+
async def process_candidate(candidate):
|
| 269 |
+
"""Execute the per-candidate checklist and state transitions."""
|
| 270 |
+
state = await load_candidate_state(candidate.id)
|
| 271 |
+
|
| 272 |
+
if state == "cv_uploaded":
|
| 273 |
+
await parse_and_screen_cv(candidate)
|
| 274 |
+
elif state == "screened":
|
| 275 |
+
await schedule_interview(candidate)
|
| 276 |
+
# ... additional states here
|
| 277 |
+
|
| 278 |
+
await save_candidate_state(candidate.id, state)
|
| 279 |
+
print(f"✅ Candidate {candidate.id} processed ({state})")
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
# --- Group-level concurrent handler ---
|
| 283 |
+
async def process_group(candidates: List, group_name: str):
|
| 284 |
+
"""Handle all candidates in one group concurrently."""
|
| 285 |
+
print(f"⚙️ Processing group: {group_name} ({len(candidates)} candidates)")
|
| 286 |
+
tasks = [process_candidate(c) for c in candidates]
|
| 287 |
+
await asyncio.gather(*tasks)
|
| 288 |
+
print(f"✅ Group {group_name} completed")
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
# --- Main supervisor orchestration ---
|
| 292 |
+
async def supervisor_run():
|
| 293 |
+
"""Supervisor orchestrates all concurrent candidate workflows."""
|
| 294 |
+
print("🧠 Supervisor initialized")
|
| 295 |
+
|
| 296 |
+
# Query database and classify candidates
|
| 297 |
+
report = await get_candidate_report()
|
| 298 |
+
new_candidates = report["new"]
|
| 299 |
+
screened_candidates = report["screened"]
|
| 300 |
+
|
| 301 |
+
# Launch group workflows concurrently
|
| 302 |
+
await asyncio.gather(
|
| 303 |
+
process_group(new_candidates, "new_candidates"),
|
| 304 |
+
process_group(screened_candidates, "screened_candidates")
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
print("🎯 All candidate groups processed successfully")
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
# --- Entry point ---
|
| 311 |
+
if __name__ == "__main__":
|
| 312 |
+
asyncio.run(supervisor_run())
|
| 313 |
+
```
|
| 314 |
+
|
| 315 |
+
---
|
| 316 |
+
***Key Properties***
|
| 317 |
+
- **Async orchestration, single agent:** The supervisor coordinates all tasks without duplicating reasoning contexts.
|
| 318 |
+
- **Per-candidate determinism:** Each checklist/state file is loaded, updated, and written atomically.
|
| 319 |
+
- **Parallel group execution:** New and screened candidates can be processed simultaneously.
|
| 320 |
+
- **Scalability path:** The same async structure can later integrate with LangGraph’s parallel nodes or distributed queues.
|
docs/agents/supervisor/mvps/mvp2/single_candidate_mvp.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# ***Single-Candidate MVP — Tick Box Milestones***
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
This V1 map outlines the simplest complete workflow for the HR agent system: HR enters the UI, receives a clear overview of all candidates and their screening status, and then chooses one of two actions—process new applicants or continue with those who are already screened. The supervisor then carries out the chosen workflow step-by-step, invoking the appropriate subagents (screening, Gmail, calendar) and updating each candidate’s state accordingly.
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
## **V2 Workflow Diagram — HR → Supervisor → Actions**
|
| 9 |
+
```mermaid
|
| 10 |
+
flowchart TD
|
| 11 |
+
|
| 12 |
+
HR[HR opens UI] --> Q[Supervisor generates status report: applicants, screened, passed, failed]
|
| 13 |
+
|
| 14 |
+
Q --> HR_Decision{HR chooses action}
|
| 15 |
+
|
| 16 |
+
HR_Decision --> A[Option A: Process new applicant]
|
| 17 |
+
HR_Decision --> B[Option B: Process screened applicant]
|
| 18 |
+
|
| 19 |
+
%% Option A path
|
| 20 |
+
A --> A1[Supervisor triggers screening]
|
| 21 |
+
A1 --> A2[Subagent screens candidate]
|
| 22 |
+
A2 --> A3[Screening result stored in DB]
|
| 23 |
+
A3 --> A4[Candidate status updated]
|
| 24 |
+
|
| 25 |
+
%% Option B path
|
| 26 |
+
B --> B_DECISION{Candidate accepted or declined?}
|
| 27 |
+
|
| 28 |
+
B_DECISION --> B1A[If accepted: Gmail + Calendar workflow]
|
| 29 |
+
B_DECISION --> B1B[If declined: Gmail rejection email]
|
| 30 |
+
|
| 31 |
+
%% End nodes
|
| 32 |
+
A4 --> END((Done))
|
| 33 |
+
B1A --> END
|
| 34 |
+
B1B --> END
|
| 35 |
+
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
# Single-Candidate MVP — General Tick Box Milestones
|
| 39 |
+
|
| 40 |
+
## **1) CV Upload + Candidate Storage**
|
| 41 |
+
- [ ] Candidate record created in DB (manual trigger OK)
|
| 42 |
+
- [ ] CV file uploaded (manual OK for MVP)
|
| 43 |
+
- [ ] CV parsed into structured format
|
| 44 |
+
- [ ] Parsed CV stored in DB linked to candidate
|
| 45 |
+
- [ ] Candidate status set to `uploaded`
|
| 46 |
+
|
| 47 |
+
---
|
| 48 |
+
|
| 49 |
+
## **2) Supervisor + Basic UI (Single Entry Point)**
|
| 50 |
+
|
| 51 |
+
### **2.1 HR Status Query & Reporting**
|
| 52 |
+
- [ ] Supervisor responds to: “What is the current status quo?”
|
| 53 |
+
- [ ] Supervisor reports total number of applicants
|
| 54 |
+
- [ ] Supervisor reports how many have been screened
|
| 55 |
+
- [ ] Supervisor reports how many passed vs failed screening
|
| 56 |
+
- [ ] Supervisor presents results as a clear HR-friendly summary report
|
| 57 |
+
|
| 58 |
+
### **2.2 Supervisor Actions**
|
| 59 |
+
- [ ] “Process new applicant” option available
|
| 60 |
+
- [ ] “Process screened applicant” option available
|
| 61 |
+
|
| 62 |
+
### **2.3 Action Logic**
|
| 63 |
+
- [ ] Choosing “process new applicant” triggers initial screening workflow
|
| 64 |
+
- [ ] Choosing “process screened applicant” triggers:
|
| 65 |
+
- [ ] Gmail + calendar workflow for accepted candidates
|
| 66 |
+
- [ ] Gmail rejection workflow for declined candidates
|
| 67 |
+
|
| 68 |
+
### **2.4 State Updates**
|
| 69 |
+
- [ ] Supervisor updates candidate status after each action
|
| 70 |
+
- [ ] Supervisor logs each action outcome into DB
|
| 71 |
+
|
| 72 |
+
---
|
| 73 |
+
|
| 74 |
+
## **3) CV Screening Subagent**
|
| 75 |
+
- [ ] Subagent loads candidate record
|
| 76 |
+
- [ ] Subagent loads stored CV text
|
| 77 |
+
- [ ] Subagent performs CV screening
|
| 78 |
+
- [ ] Structured screening result saved to DB
|
| 79 |
+
- [ ] Candidate status updated to `screened`
|
| 80 |
+
- [ ] Supervisor UI reflects updated screening status
|
| 81 |
+
|
| 82 |
+
---
|
| 83 |
+
|
| 84 |
+
## **4) Per-Candidate Deterministic State Machine**
|
| 85 |
+
- [ ] Each candidate has a dedicated state object (e.g., `state: "cv_uploaded"`)
|
| 86 |
+
- [ ] State object persisted in DB or file
|
| 87 |
+
- [ ] Supervisor reads this state before taking actions
|
| 88 |
+
- [ ] Supervisor updates the state after actions
|
| 89 |
+
- [ ] Workflow remains predictable and restartable per candidate
|
| 90 |
+
- [ ] Each candidate’s context is isolated (no cross-candidate bleed)
|
| 91 |
+
|
| 92 |
+
---
|
| 93 |
+
|
| 94 |
+
## **5) Per-Candidate Checklist File**
|
| 95 |
+
- [ ] A Markdown checklist file is created per candidate
|
| 96 |
+
- [ ] Checklist is updated at each atomic step
|
| 97 |
+
- [ ] Supervisor loads checklist to determine next required action
|
| 98 |
+
- [ ] Checklist mirrors real workflow steps (upload → screening → notification → scheduling)
|
| 99 |
+
- [ ] Checklist allows deterministic progress tracking
|
| 100 |
+
- [ ] Checklist helps ensure one atomic action per supervisor step
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
---
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
# 🧭 Incremental Implementation Roadmap — From Skeleton to MVP
|
| 108 |
+
|
| 109 |
+
This roadmap breaks the HR Agent MVP into incremental, testable phases.
|
| 110 |
+
Each phase builds on the previous one — keeping the system functional at every step while increasing autonomy, determinism, and traceability.
|
| 111 |
+
|
| 112 |
+
A key invariant across all phases:
|
| 113 |
+
> 🧩 **Each atomic action must update the per-candidate Markdown checklist**
|
| 114 |
+
> The checklist is the single source of truth for progress, state recovery, and auditability.
|
| 115 |
+
|
| 116 |
+
***Checklist:***
|
| 117 |
+
```text
|
| 118 |
+
# Candidate Checklist — ID 123
|
| 119 |
+
|
| 120 |
+
- [ ] CV uploaded, parsed and stored
|
| 121 |
+
- [ ] CV screening (pass / fail)
|
| 122 |
+
- [ ] Screening results notified to candidate & HR
|
| 123 |
+
- [ ] Asked candidate for available time slots
|
| 124 |
+
- [ ] Received candidate availability
|
| 125 |
+
- [ ] Checked HR availability
|
| 126 |
+
- [ ] Scheduled interview
|
| 127 |
+
- [ ] Final confirmation email sent
|
| 128 |
+
```
|
| 129 |
+
---
|
| 130 |
+
|
| 131 |
+
## **Phase 1 — Foundation Layer: Candidate I/O + Static State**
|
| 132 |
+
|
| 133 |
+
Goal: Establish basic data persistence and candidate representation.
|
| 134 |
+
Outcome: You can manually create a candidate, attach a CV, and store structured data.
|
| 135 |
+
|
| 136 |
+
- [ ] DB schema or JSON store for candidates
|
| 137 |
+
- `candidate_id`, `name`, `cv_path`, `state`, `screening_result`
|
| 138 |
+
- [ ] Manual script or simple UI form to create a candidate
|
| 139 |
+
- [ ] Upload CV file (manual or API endpoint)
|
| 140 |
+
- [ ] Parse CV into structured format
|
| 141 |
+
- [ ] Store parsed CV in DB
|
| 142 |
+
- [ ] Candidate state set to `"cv_uploaded"`
|
| 143 |
+
- [ ] ✅ **Checklist updated after each atomic step:**
|
| 144 |
+
- `[x] CV uploaded`
|
| 145 |
+
- `[x] CV parsed`
|
| 146 |
+
- `[x] Candidate stored`
|
| 147 |
+
|
| 148 |
+
---
|
| 149 |
+
|
| 150 |
+
## **Phase 2 — Supervisor Core Logic (CLI or Barebones UI)**
|
| 151 |
+
|
| 152 |
+
Goal: Implement the supervisor agent’s reasoning + reporting loop for one candidate.
|
| 153 |
+
Outcome: HR can ask status questions and issue simple actions.
|
| 154 |
+
|
| 155 |
+
- [ ] Simple command interface or Streamlit-like dashboard
|
| 156 |
+
- [ ] Supervisor responds to: “What’s the current status quo?”
|
| 157 |
+
- [ ] Supervisor reports:
|
| 158 |
+
- [ ] Total number of applicants
|
| 159 |
+
- [ ] Number screened
|
| 160 |
+
- [ ] Number passed vs failed
|
| 161 |
+
- [ ] HR can issue commands:
|
| 162 |
+
- [ ] `process_new_applicant`
|
| 163 |
+
- [ ] `process_screened_applicant`
|
| 164 |
+
- [ ] Supervisor executes one step → updates candidate state
|
| 165 |
+
- [ ] State transitions logged
|
| 166 |
+
- [ ] ✅ **Checklist updated automatically after each supervisor action**
|
| 167 |
+
|
| 168 |
+
---
|
| 169 |
+
|
| 170 |
+
## **Phase 3 — Screening Subagent Integration**
|
| 171 |
+
|
| 172 |
+
Goal: Automate CV evaluation and store results.
|
| 173 |
+
Outcome: Supervisor delegates screening to subagent and records outcome.
|
| 174 |
+
|
| 175 |
+
- [ ] Subagent loads candidate record + parsed CV
|
| 176 |
+
- [ ] Runs screening model (mock or real)
|
| 177 |
+
- [ ] Produces structured result (e.g., `{passed: True, score: 85}`)
|
| 178 |
+
- [ ] Saves result in DB
|
| 179 |
+
- [ ] Candidate state transitions:
|
| 180 |
+
- `"cv_uploaded"` → `"screened_passed"` or `"screened_failed"`
|
| 181 |
+
- [ ] Supervisor report reflects updated screening counts
|
| 182 |
+
- [ ] ✅ **Checklist updated:**
|
| 183 |
+
- `[x] Screening started`
|
| 184 |
+
- `[x] Screening completed`
|
| 185 |
+
- `[x] Result stored`
|
| 186 |
+
|
| 187 |
+
---
|
| 188 |
+
|
| 189 |
+
## **Phase 4 — Candidate Communication Layer (Gmail Integration)**
|
| 190 |
+
|
| 191 |
+
Goal: Automate candidate notifications based on screening results.
|
| 192 |
+
Outcome: Supervisor triggers Gmail subagent to send emails.
|
| 193 |
+
|
| 194 |
+
- [ ] Gmail subagent setup
|
| 195 |
+
- [ ] For `screened_failed`: send rejection email
|
| 196 |
+
- [ ] For `screened_passed`: send congratulations + request time slots
|
| 197 |
+
- [ ] Email status stored in DB
|
| 198 |
+
- [ ] Candidate state transitions:
|
| 199 |
+
- `"screened_failed"` → `"notified_rejection"`
|
| 200 |
+
- `"screened_passed"` → `"awaiting_time_slots"`
|
| 201 |
+
- [ ] ✅ **Checklist updated:**
|
| 202 |
+
- `[x] Candidate notified of result`
|
| 203 |
+
- `[x] Email status recorded`
|
| 204 |
+
|
| 205 |
+
---
|
| 206 |
+
|
| 207 |
+
## **Phase 5 — Interview Scheduling Layer (Calendar Integration)**
|
| 208 |
+
|
| 209 |
+
Goal: Automate scheduling for passed candidates.
|
| 210 |
+
Outcome: Supervisor matches availability and books meetings.
|
| 211 |
+
|
| 212 |
+
- [ ] Calendar subagent setup
|
| 213 |
+
- [ ] Candidate time slot parsing (from email reply or mock)
|
| 214 |
+
- [ ] Check HR calendar availability
|
| 215 |
+
- [ ] Schedule meeting and send confirmation email
|
| 216 |
+
- [ ] Candidate state transitions:
|
| 217 |
+
- `"awaiting_time_slots"` → `"interview_scheduled"`
|
| 218 |
+
- [ ] ✅ **Checklist updated:**
|
| 219 |
+
- `[x] Candidate provided time slots`
|
| 220 |
+
- `[x] HR availability checked`
|
| 221 |
+
- `[x] Interview scheduled`
|
| 222 |
+
- `[x] Confirmation sent`
|
| 223 |
+
|
| 224 |
+
---
|
| 225 |
+
|
| 226 |
+
# Advanced System Features - Future Directions for Next MVP
|
| 227 |
+
|
| 228 |
+
## **Phase 6 — Determinism + Persistence Layer**
|
| 229 |
+
|
| 230 |
+
Goal: Make the system predictable, restartable, and auditable.
|
| 231 |
+
Outcome: Each candidate runs in isolation with explicit state and checklist.
|
| 232 |
+
|
| 233 |
+
- [ ] Per-candidate state object stored in DB or JSON
|
| 234 |
+
- [ ] Per-candidate Markdown checklist file created:
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
## **Phase 7 — Async Supervisor Orchestration (Scaling Up)**
|
| 238 |
+
|
| 239 |
+
Goal: Extend single-candidate flow to multiple candidates concurrently.
|
| 240 |
+
Outcome: Async orchestration across candidate groups.
|
| 241 |
+
|
| 242 |
+
- [ ] Async supervisor runs concurrently across groups:
|
| 243 |
+
- [ ] New candidates → screening path
|
| 244 |
+
- [ ] Screened candidates → scheduling path
|
| 245 |
+
- [ ] Each candidate processed in an isolated coroutine
|
| 246 |
+
- [ ] Checklist + state ensure deterministic behavior per candidate
|
| 247 |
+
- [ ] Supervisor reports real-time progress
|
| 248 |
+
- [ ] Single reasoning context preserved
|
| 249 |
+
- [ ] ✅ **Each candidate’s checklist still governs progression, ensuring isolation and safe concurrency**
|
| 250 |
+
- [ ] Supervisor awaits all async tasks and compiles final HR summary report
|
| 251 |
+
- [ ] System supports safe restarts (resume from last known checklist state)
|
| 252 |
+
- [ ] Scalable to LangGraph or distributed queue orchestration later on
|
| 253 |
+
|
| 254 |
+
This roadmap breaks the HR Agent MVP into incremental, testable phases.
|
| 255 |
+
Each phase builds logically on the previous one — so the system remains functional at every step while increasing in autonomy and complexity.
|
| 256 |
+
|
| 257 |
+
---
|
docs/agents/supervisor/mvps/mvp2/tools_needed.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ⚙️ Tools Required for MVP (Phases 1–5)
|
| 2 |
+
|
| 3 |
+
The system consists of a **Supervisor Agent** (central orchestrator) and several **specialized subagents**.
|
| 4 |
+
Each tool corresponds to a specific capability needed for the single-candidate MVP flow — from CV upload to interview scheduling.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## 🧠 **Supervisor Agent (Core Orchestrator)**
|
| 9 |
+
|
| 10 |
+
### **Role**
|
| 11 |
+
Acts as the **central controller**:
|
| 12 |
+
- Interfaces with HR (via UI or chat)
|
| 13 |
+
- Handles reasoning, status reporting, and command interpretation
|
| 14 |
+
- Orchestrates subagents (screening, Gmail, calendar)
|
| 15 |
+
- Updates both the database and checklist after each atomic action
|
| 16 |
+
|
| 17 |
+
### **Tools Required**
|
| 18 |
+
|
| 19 |
+
| Tool | Purpose | Used in Phase |
|
| 20 |
+
|------|----------|---------------|
|
| 21 |
+
| 🗃️ **Database Tool / ORM Adapter** | Read, create, and update `Candidate` records (status, CV paths, results). | 1–5 |
|
| 22 |
+
| 📄 **File I/O Tool** | Read/write per-candidate checklist and parsed CV files. | 1–5 |
|
| 23 |
+
| 📊 **Reporting Helper / Aggregation Utility** | Query DB and summarize candidate counts (new, screened, passed, failed). | 2 |
|
| 24 |
+
| 🧩 **Subagent Dispatch Interface** | Send structured tasks to subagents (screening, Gmail, calendar). | 3–5 |
|
| 25 |
+
| 🧱 **State Manager** | Load and update candidate’s deterministic state object (`state`, `checklist`, `status`). | 4–5 |
|
| 26 |
+
| 🕵️ **HR Command Parser / Intent Handler** | Interpret HR’s natural-language instructions (e.g., “process new applicants”). | 2 |
|
| 27 |
+
|
| 28 |
+
---
|
| 29 |
+
|
| 30 |
+
## 🤖 **Subagents & Their Tools**
|
| 31 |
+
|
| 32 |
+
### **1. CV Screening Subagent**
|
| 33 |
+
**Phase:** 3
|
| 34 |
+
**Purpose:** Automatically screen and score CVs.
|
| 35 |
+
|
| 36 |
+
| Tool | Purpose |
|
| 37 |
+
|------|----------|
|
| 38 |
+
| 📄 **CV Parser** | Extract structured text from uploaded CV (PDF/DOCX). |
|
| 39 |
+
| 🧮 **Screening Model / Classifier** | Evaluate parsed CV using rules or an ML/LLM-based model. |
|
| 40 |
+
| 🗃️ **DB Access Tool** | Save screening results and update `Candidate.status = cv_screened`. |
|
| 41 |
+
| 📄 **Checklist Writer** | Mark `[x] Screening started` and `[x] Screening completed` in the checklist. |
|
| 42 |
+
|
| 43 |
+
---
|
| 44 |
+
|
| 45 |
+
### **2. Gmail Subagent**
|
| 46 |
+
**Phase:** 4
|
| 47 |
+
**Purpose:** Send automated emails to candidates based on screening results.
|
| 48 |
+
|
| 49 |
+
| Tool | Purpose |
|
| 50 |
+
|------|----------|
|
| 51 |
+
| 📬 **Gmail API Wrapper** | Send templated emails (rejection or invitation). |
|
| 52 |
+
| 🧠 **Template Manager** | Store and select appropriate email templates. |
|
| 53 |
+
| 🗃️ **DB Access Tool** | Record email activity and update candidate state. |
|
| 54 |
+
| 📄 **Checklist Writer** | Mark `[x] Candidate notified` and `[x] Email status recorded`. |
|
| 55 |
+
|
| 56 |
+
---
|
| 57 |
+
|
| 58 |
+
### **3. Calendar / Scheduling Subagent**
|
| 59 |
+
**Phase:** 5
|
| 60 |
+
**Purpose:** Automate interview scheduling for passed candidates.
|
| 61 |
+
|
| 62 |
+
| Tool | Purpose |
|
| 63 |
+
|------|----------|
|
| 64 |
+
| 🗓️ **Google Calendar API Wrapper** | Retrieve HR availability and create interview events. |
|
| 65 |
+
| 📬 **Gmail API (reuse)** | Send scheduling confirmations and time slot requests. |
|
| 66 |
+
| 🧠 **Availability Matcher** | Compare candidate-provided slots vs HR calendar availability. |
|
| 67 |
+
| 🗃️ **DB Access Tool** | Update `Candidate.status = interview_scheduled`. |
|
| 68 |
+
| 📄 **Checklist Writer** | Mark `[x] HR availability checked`, `[x] Interview scheduled`, `[x] Confirmation sent`. |
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## 🧾 **Cross-Cutting Utilities (Shared by All Agents)**
|
| 73 |
+
|
| 74 |
+
| Utility | Purpose | Used by |
|
| 75 |
+
|----------|----------|----------|
|
| 76 |
+
| 🧩 **Checklist Manager** | CRUD operations on per-candidate Markdown checklist files (load, mark, persist). | All |
|
| 77 |
+
| 🧱 **State Sync Layer** | Sync checklist milestone boundaries with DB `status` updates. | Supervisor |
|
| 78 |
+
| ⏱️ **Logging & Audit Utility** | Record all actions, errors, and state transitions. | All |
|
| 79 |
+
| 🧮 **Config / Environment Loader** | Manage API keys, paths, and credentials for Gmail & Calendar. | All networked agents |
|
| 80 |
+
|
| 81 |
+
---
|
| 82 |
+
|
| 83 |
+
## 🧭 **Tool Overview by Phase**
|
| 84 |
+
|
| 85 |
+
| Phase | Supervisor Tools | Subagents / Tools |
|
| 86 |
+
|-------|-------------------|------------------|
|
| 87 |
+
| **1 — Candidate I/O + Storage** | DB Adapter, File I/O, Checklist Manager | CV Parser (manual for MVP) |
|
| 88 |
+
| **2 — Supervisor + UI** | HR Command Parser, DB Reporter, Checklist Manager | — |
|
| 89 |
+
| **3 — CV Screening** | Subagent Dispatcher, Checklist Manager | CV Parser, Screening Model, DB Writer |
|
| 90 |
+
| **4 — Candidate Communication** | Subagent Dispatcher, Checklist Manager | Gmail API, Template Manager |
|
| 91 |
+
| **5 — Interview Scheduling** | Subagent Dispatcher, Checklist Manager | Calendar API, Availability Matcher, Gmail API |
|
| 92 |
+
|
| 93 |
+
---
|
| 94 |
+
|
| 95 |
+
## ✅ **Summary**
|
| 96 |
+
|
| 97 |
+
- **Supervisor Agent Tools:**
|
| 98 |
+
- DB Adapter
|
| 99 |
+
- Checklist Manager
|
| 100 |
+
- HR Interface (UI or CLI)
|
| 101 |
+
- Subagent Dispatcher
|
| 102 |
+
- State Sync Logic
|
| 103 |
+
|
| 104 |
+
- **Subagents:**
|
| 105 |
+
- **Screening Subagent:** CV Parser + Screening Model
|
| 106 |
+
- **Gmail Subagent:** Email Templating + Send API
|
| 107 |
+
- **Calendar Subagent:** Scheduling + Availability Matching
|
| 108 |
+
|
| 109 |
+
Together, these tools form the complete single-candidate MVP pipeline —
|
| 110 |
+
from candidate intake → CV screening → communication → interview scheduling.
|
docs/agents/supervisor/mvps/overview.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# **Application Flow**
|
| 2 |
+
|
| 3 |
+
## **MVP Version #2**
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
```mermaid
|
| 7 |
+
flowchart TD
|
| 8 |
+
|
| 9 |
+
A[CV Upload] --> B[Store & Parse CV]
|
| 10 |
+
|
| 11 |
+
B --> C[Trigger: CV Screening]
|
| 12 |
+
|
| 13 |
+
C --> D[Supervisor Delegates<br/>Screening to Subagent]
|
| 14 |
+
|
| 15 |
+
D --> E[Write Results to DB]
|
| 16 |
+
|
| 17 |
+
E --> F[Supervisor Receives Screening Results]
|
| 18 |
+
|
| 19 |
+
F --> G{Did Candidate Pass<br/>CV Screening?}
|
| 20 |
+
|
| 21 |
+
G -- No --> H[Notify Candidate & HR:<br/>Application Rejected]
|
| 22 |
+
|
| 23 |
+
G -- Yes --> I[Notify Candidate & HR:<br/>Passed Screening]
|
| 24 |
+
|
| 25 |
+
I --> J[Request Candidate's<br/>Available Time Slots]
|
| 26 |
+
|
| 27 |
+
J --> K[Check HR Calendar<br/>Availability]
|
| 28 |
+
|
| 29 |
+
K --> L[Schedule Person-Person<br/>Interview]
|
| 30 |
+
|
| 31 |
+
L --> M[Notify HR & Candidate<br/>Interview Confirmed]
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
The first goal is to ensure the application flows and works autonomously as follows:
|
| 35 |
+
|
| 36 |
+
### **1) CV Submission**
|
| 37 |
+
- Applicant submits CV
|
| 38 |
+
- CV is parsed and stored
|
| 39 |
+
|
| 40 |
+
### **2) CV Screening**
|
| 41 |
+
- Supervisor agent becomes aware that a CV was uploaded
|
| 42 |
+
- A **“CV was uploaded” signal** triggers the supervisor to delegate **CV screening** to a sub-agent
|
| 43 |
+
- Results are written to the database & candidate status is updated
|
| 44 |
+
- A **“CV was screened” signal** notifies the supervisor that results are available and is able to read the results from db
|
| 45 |
+
|
| 46 |
+
### **3) Success / Failure Notification**
|
| 47 |
+
|
| 48 |
+
Based on CV screening results that the supervisor injected into its context, it decides:
|
| 49 |
+
|
| 50 |
+
#### **a) Not Passed**
|
| 51 |
+
- Trigger sub-agent to notify candidate *and* HR via email that the candidate did not meet requirements
|
| 52 |
+
|
| 53 |
+
#### **b) Passed**
|
| 54 |
+
- Trigger sub-agent to:
|
| 55 |
+
- Notify candidate and HR of the successful CV screening
|
| 56 |
+
- Ask candidate to provide several available time slots
|
| 57 |
+
- Check HR’s available time slots in their calendar
|
| 58 |
+
- Schedule a meeting based on overlapping availability
|
| 59 |
+
- Notify HR that candidate X passed CV screening and that an interview was scheduled on **`dd-mm-yyyy`**
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
## **Final**
|
| 64 |
+
*(To be defined)*
|
docs/agents/supervisor/supervisor_general.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Supervisor Agent Overview
|
| 2 |
+
This document explains the role, behavior, and context-engineering strategy of the Supervisor Agent used in the agentic HR recruitment system. It describes how the supervisor plans, coordinates, delegates, and adapts the multi-agent workflow, and how context is compressed, summarized, and managed to maintain robustness across long-running interactions.
|
| 3 |
+
|
| 4 |
+
It also explains context-engineering strategies that are utilized in order to reduce token usage along with increasing reliability and task completion rate of the agents.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## 1. Purpose of the Supervisor Agent
|
| 9 |
+
The Supervisor Agent serves as the centralized orchestrator responsible for maintaining global workflow control. Its role is to ensure that each subagent operates in the correct order, under the correct conditions, with the right context, and with full visibility into progress and failures.
|
| 10 |
+
|
| 11 |
+
At a high level, the supervisor is responsible for:
|
| 12 |
+
- Generating and maintaining the end-to-end hiring plan.
|
| 13 |
+
- Determining which subagent should execute next.
|
| 14 |
+
- Providing stateful context to each subagent.
|
| 15 |
+
- Performing adaptive re-planning when outputs or conditions change.
|
| 16 |
+
- Managing memory summaries and preventing context pollution.
|
| 17 |
+
- Producing explainable logs and reasoning traces for the dashboard.
|
| 18 |
+
|
| 19 |
+
This agent ensures that the entire HR pipeline behaves autonomously while remaining transparent, safe, and resilient.
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## 2. High-Level Workflow
|
| 24 |
+
The supervisor follows a structured, plan-driven execution model. The default plan is:
|
| 25 |
+
|
| 26 |
+
1. CV Screening
|
| 27 |
+
2. Voice Screening
|
| 28 |
+
3. HR Interview Scheduling
|
| 29 |
+
4. Final Decision Report
|
| 30 |
+
|
| 31 |
+
However, execution is not strictly linear. The supervisor can skip, re-order, repeat, or halt steps based on subagent outputs.
|
| 32 |
+
|
| 33 |
+
### Workflow Model
|
| 34 |
+
1. **Initialize Plan**
|
| 35 |
+
- Build an initial sequence of workflow stages based on available candidate data.
|
| 36 |
+
- Load any relevant memory summaries or past runs from the database.
|
| 37 |
+
|
| 38 |
+
2. **Select Next Stage**
|
| 39 |
+
- Evaluate current state.
|
| 40 |
+
- Choose the next subagent based on plan progress and real-time results.
|
| 41 |
+
|
| 42 |
+
3. **Construct Subagent Context Package**
|
| 43 |
+
- Provide the subagent with the minimal context they need:
|
| 44 |
+
- Candidate details
|
| 45 |
+
- Results from prior stages
|
| 46 |
+
- Relevant memory summaries
|
| 47 |
+
- Tool call history (when helpful)
|
| 48 |
+
- Current workflow goal
|
| 49 |
+
|
| 50 |
+
4. **Invoke the Subagent**
|
| 51 |
+
- The selected agent executes a task using LangGraph tool calls or MCP integrations.
|
| 52 |
+
- Outputs are validated and stored in state and database.
|
| 53 |
+
|
| 54 |
+
5. **Reflect and Update Plan**
|
| 55 |
+
- The supervisor generates a lightweight reflection summary:
|
| 56 |
+
"What happened?", "Is the result valid?", "What is needed next?"
|
| 57 |
+
- If conditions changed, the supervisor updates the plan accordingly.
|
| 58 |
+
Examples:
|
| 59 |
+
- Skip voice screening if CV screening fails.
|
| 60 |
+
- Retry scheduling if the calendar shows no available slots.
|
| 61 |
+
- Pause and request HR confirmation when required.
|
| 62 |
+
|
| 63 |
+
6. **Persist Memory**
|
| 64 |
+
- Summaries, transcripts, evaluations, and structured results are stored to prevent context bloating.
|
| 65 |
+
- Only relevant compact memory is injected back into future steps.
|
| 66 |
+
|
| 67 |
+
7. **Repeat Until Complete**
|
| 68 |
+
|
| 69 |
+
---
|
| 70 |
+
|
| 71 |
+
## 3. Task Delegation Strategy
|
| 72 |
+
The supervisor delegates tasks to subagents based on rule-based planning combined with lightweight LLM reasoning.
|
| 73 |
+
|
| 74 |
+
### Delegation Logic
|
| 75 |
+
- **CV Screening Agent** is invoked when a new applicant is added or a CV is updated.
|
| 76 |
+
- **Voice Screening Agent** is invoked when the candidate passes CV screening and HR or the plan flags them as suitable for a phone screen.
|
| 77 |
+
- **Scheduler Agent** is invoked once voice screening produces a valid transcript and evaluation.
|
| 78 |
+
- **Decision Agent** is invoked after all stages complete, or when early rejection is clear.
|
| 79 |
+
|
| 80 |
+
Each subagent returns structured outputs that the supervisor uses to drive the next step.
|
| 81 |
+
|
| 82 |
+
---
|
| 83 |
+
|
| 84 |
+
## 4. Adaptive Re-Planning
|
| 85 |
+
One of the supervisor’s core responsibilities is to respond dynamically to real-world conditions.
|
| 86 |
+
|
| 87 |
+
Examples of adaptive behaviors:
|
| 88 |
+
|
| 89 |
+
- **Tool failure**: If Gmail or Calendar returns an MCP error, the supervisor retries, selects an alternative interaction path, or defers the step.
|
| 90 |
+
- **Calendar constraints**: If no timeslots are available, the supervisor generates an alternate plan.
|
| 91 |
+
- **Candidate status shifts**: If a candidate responds late or provides new documents, the supervisor reevaluates the plan.
|
| 92 |
+
- **Voice call failure**: If the candidate does not pick up, the supervisor schedules a retry or sends an email follow-up.
|
| 93 |
+
|
| 94 |
+
This makes the workflow robust, autonomous, and consistent with agentic paradigms.
|
| 95 |
+
|
| 96 |
+
In unique situations, such as exceptional applicants, the agent can accellerate the timeline such as skipping stages. This is entirely discretionary and up to the agent's decision.
|
| 97 |
+
|
| 98 |
+
---
|
| 99 |
+
|
| 100 |
+
## 5. Context Engineering Strategy
|
| 101 |
+
We utilize context engineering many times in the workflow. Each subagent instance is stateless, meaning that it doesn't retain any memory or context from previous turns. This allows for reduced token usage, since information from previous applicants is not relevant for new tasks. This state is reset every time it handles a unique task, but is retained until the supervisor determines the subagent has fully completed all necessary steps.
|
| 102 |
+
|
| 103 |
+
Each time a subagent completes a task, the agent is given the full response. After the supervisor performs its next action, the subagent response is compacted into a high-level summary.
|
| 104 |
+
|
| 105 |
+
The supervisor agent itself is also stateless in a sense: its context is applicant-dependant. Real-life timelines are messy, and multiple applicants will have steps overlap. Each time the supervisor agent needs to perform a new task, it loads the context from the user it is working on, and unloads it when it is done. This way, the supervisor is able to stay focused on a specific applicant and not get confused or distracted by managing multiple applicants at the same time. The state for this agent is saved in temporary files which are deleted once the applicant has a final decision made (hire or discard).
|
| 106 |
+
|
| 107 |
+
---
|
| 108 |
+
|
| 109 |
+
## 6. Explainability and Dashboard Integration
|
| 110 |
+
The supervisor produces the trace outputs that power the Gradio dashboard.
|
| 111 |
+
|
| 112 |
+
This includes:
|
| 113 |
+
- Plan state (past, current, future)
|
| 114 |
+
- Tool call logs
|
| 115 |
+
- Reasoning and reflection summaries
|
| 116 |
+
- Active memory excerpts
|
| 117 |
+
- Subagent outputs
|
| 118 |
+
- Error messages and fallback paths
|
| 119 |
+
|
| 120 |
+
Because a single agent manages the plan, dashboard integration remains consistent and interpretable.
|
docs/agents/voice_screening.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Voice Screening MVP
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
The **Voice Screening MVP** provides a simple browser-based voice interview interface using Streamlit and OpenAI Realtime API. This is a simplified implementation that removes the complexity of LangGraph agents, Twilio telephony, and FastAPI servers.
|
| 6 |
+
|
| 7 |
+
## Architecture
|
| 8 |
+
|
| 9 |
+
**Simple MVP Architecture:**
|
| 10 |
+
- **Streamlit UI**: Web interface with toggle recording button
|
| 11 |
+
- **WebSocket Proxy**: FastAPI proxy for browser WebSocket authentication
|
| 12 |
+
- **OpenAI Realtime API**: Real-time speech-to-speech via WebSocket
|
| 13 |
+
- **Real-time transcription**: Live transcript display
|
| 14 |
+
- **Real-time TTS**: Audio playback in browser with sequential queue
|
| 15 |
+
- **Simple backend**: Post-session analysis and database storage
|
| 16 |
+
|
| 17 |
+
## Components
|
| 18 |
+
|
| 19 |
+
| Component | Purpose |
|
| 20 |
+
|------------|----------|
|
| 21 |
+
| **Streamlit UI** | Main interface with interview controls and transcript display |
|
| 22 |
+
| **HTML/JavaScript Component** | WebSocket connection via proxy, audio recording/playback with queue |
|
| 23 |
+
| **WebSocket Proxy** | FastAPI service to handle OpenAI authentication (browsers can't set custom headers) |
|
| 24 |
+
| **OpenAI Realtime API** | Handles speech-to-text and text-to-speech in real-time (gpt-4o-mini-realtime-preview) |
|
| 25 |
+
| **Analysis Function** | Simple GPT-4 analysis of transcript (no LangGraph) |
|
| 26 |
+
| **Database Utilities** | Save results to database |
|
| 27 |
+
|
| 28 |
+
## Flow
|
| 29 |
+
|
| 30 |
+
```text
|
| 31 |
+
User enters email and requests authentication code
|
| 32 |
+
↓
|
| 33 |
+
Proxy generates 6-digit code (MVP: displayed directly; production: sent via email/SMS)
|
| 34 |
+
↓
|
| 35 |
+
User enters email and code to verify
|
| 36 |
+
↓
|
| 37 |
+
Proxy validates code and returns session token
|
| 38 |
+
↓
|
| 39 |
+
User clicks "Start Interview"
|
| 40 |
+
↓
|
| 41 |
+
Browser opens WebSocket to WebSocket Proxy (with session token in query param)
|
| 42 |
+
↓
|
| 43 |
+
Proxy validates session token
|
| 44 |
+
↓
|
| 45 |
+
Proxy forwards connection to OpenAI Realtime API with API key authentication
|
| 46 |
+
↓
|
| 47 |
+
Proxy configures OpenAI session (modalities, instructions, voice, etc.)
|
| 48 |
+
↓
|
| 49 |
+
Proxy sends greeting request to OpenAI
|
| 50 |
+
↓
|
| 51 |
+
Agent greets candidate (first TTS response)
|
| 52 |
+
↓
|
| 53 |
+
User clicks mic button to start recording (toggle on)
|
| 54 |
+
↓
|
| 55 |
+
Browser streams audio chunks to OpenAI via proxy
|
| 56 |
+
↓
|
| 57 |
+
OpenAI returns transcriptions + TTS audio in real-time
|
| 58 |
+
↓
|
| 59 |
+
Audio chunks queued and played sequentially in browser
|
| 60 |
+
↓
|
| 61 |
+
Transcript shown live in Streamlit UI
|
| 62 |
+
↓
|
| 63 |
+
User clicks mic button again to stop and send (toggle off)
|
| 64 |
+
↓
|
| 65 |
+
Audio buffer committed to OpenAI
|
| 66 |
+
↓
|
| 67 |
+
User clicks "End Interview"
|
| 68 |
+
↓
|
| 69 |
+
Send transcript to backend for analysis
|
| 70 |
+
↓
|
| 71 |
+
GPT-4 analyzes transcript (sentiment, confidence, communication)
|
| 72 |
+
↓
|
| 73 |
+
Results saved to database
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
## Implementation Details
|
| 77 |
+
|
| 78 |
+
### Streamlit UI (`src/voice_screening_ui/app.py`)
|
| 79 |
+
|
| 80 |
+
**Features:**
|
| 81 |
+
- **Authentication screen**: Email and code input fields
|
| 82 |
+
- "Start Interview" button to initialize session
|
| 83 |
+
- Toggle microphone button (click to start, click again to stop and send)
|
| 84 |
+
- Live transcript display area
|
| 85 |
+
- Session controls (end interview, logout)
|
| 86 |
+
- Analysis and results display
|
| 87 |
+
- Database integration
|
| 88 |
+
- Debug panel for connection and audio troubleshooting
|
| 89 |
+
|
| 90 |
+
**Session State:**
|
| 91 |
+
- `session_token`: Authentication token from proxy
|
| 92 |
+
- `user_email`: Authenticated user's email
|
| 93 |
+
- `session_id`: Unique interview session identifier
|
| 94 |
+
- `transcript`: List of transcript entries
|
| 95 |
+
- `is_interview_active`: Boolean flag for active session
|
| 96 |
+
- `candidate_id`: Candidate UUID
|
| 97 |
+
|
| 98 |
+
### HTML/JavaScript Component (`src/voice_screening_ui/components/voice_interface.html`)
|
| 99 |
+
|
| 100 |
+
**Features:**
|
| 101 |
+
- WebSocket connection to WebSocket Proxy with session token authentication
|
| 102 |
+
- Audio recording via browser ScriptProcessor API (PCM16)
|
| 103 |
+
- Audio playback via Web Audio API with sequential queue
|
| 104 |
+
- Real-time transcript updates
|
| 105 |
+
- Toggle recording (click to start/stop)
|
| 106 |
+
- Audio resampling from 24kHz to browser sample rate
|
| 107 |
+
- Debug panel for troubleshooting
|
| 108 |
+
|
| 109 |
+
**Key Functions:**
|
| 110 |
+
- `connectWebSocket()`: Establishes connection to WebSocket Proxy (with session token)
|
| 111 |
+
- `toggleRecording()`: Toggles recording state (start/stop)
|
| 112 |
+
- `startRecording()`: Captures microphone audio and streams to API
|
| 113 |
+
- `stopRecording()`: Stops recording and commits audio buffer
|
| 114 |
+
- `handleRealtimeMessage()`: Processes responses from OpenAI
|
| 115 |
+
- `queueAudioChunk()`: Queues audio chunks for sequential playback
|
| 116 |
+
- `processAudioQueue()`: Plays audio chunks one at a time
|
| 117 |
+
- `playAudioChunk()`: Decodes and plays individual TTS audio chunks
|
| 118 |
+
|
| 119 |
+
**Note:** Session configuration and greeting are now handled by the proxy, not the client.
|
| 120 |
+
|
| 121 |
+
### Analysis Function (`src/voice_screening_ui/analysis.py`)
|
| 122 |
+
|
| 123 |
+
**Simple function** (no LangGraph):
|
| 124 |
+
- Receives transcript text
|
| 125 |
+
- Uses OpenAI GPT-4 with structured output
|
| 126 |
+
- Returns `VoiceScreeningOutput` with scores and summary
|
| 127 |
+
- No agent nodes or graph execution
|
| 128 |
+
|
| 129 |
+
### Database Integration (`src/voice_screening_ui/utils/db.py`)
|
| 130 |
+
|
| 131 |
+
**Function:**
|
| 132 |
+
- `write_voice_results_to_db()`: Saves results to database
|
| 133 |
+
- Updates candidate status to `voice_done`
|
| 134 |
+
- Uses existing `VoiceScreeningResult` model
|
| 135 |
+
|
| 136 |
+
### WebSocket Proxy (`src/voice_screening_ui/proxy.py`)
|
| 137 |
+
|
| 138 |
+
**Features:**
|
| 139 |
+
- **Authentication endpoints**: `/auth/login` and `/auth/verify`
|
| 140 |
+
- **Session management**: In-memory token storage (MVP; use Redis/DB in production)
|
| 141 |
+
- **WebSocket proxy**: `/ws/realtime` endpoint with session token validation
|
| 142 |
+
- **Session configuration**: Handles OpenAI session setup server-side
|
| 143 |
+
- **Greeting**: Automatically sends greeting after session configuration
|
| 144 |
+
- **Health check**: `/health` endpoint for monitoring
|
| 145 |
+
|
| 146 |
+
**Authentication Flow:**
|
| 147 |
+
1. User requests code via `POST /auth/login` with email
|
| 148 |
+
2. Proxy generates 6-digit code (MVP: returns directly; production: send via email/SMS)
|
| 149 |
+
3. User verifies via `POST /auth/verify` with email and code
|
| 150 |
+
4. Proxy validates and returns session token (valid for 1 hour)
|
| 151 |
+
5. WebSocket connection requires `token` query parameter
|
| 152 |
+
|
| 153 |
+
**Session Configuration:**
|
| 154 |
+
- Moved from frontend to proxy for better security and control
|
| 155 |
+
- Configured automatically when WebSocket connects
|
| 156 |
+
- Includes modalities, instructions, voice, audio format, turn detection
|
| 157 |
+
|
| 158 |
+
## Environment Variables
|
| 159 |
+
|
| 160 |
+
```bash
|
| 161 |
+
OPENAI_API_KEY=your_openai_api_key # Required for Realtime API (stored in proxy only)
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
**Security:**
|
| 165 |
+
- API key stored in proxy environment variables (never exposed to browser)
|
| 166 |
+
- User authentication via email/code before WebSocket access
|
| 167 |
+
- Session tokens expire after 1 hour
|
| 168 |
+
- Auth codes expire after 10 minutes
|
| 169 |
+
- Proxy handles all OpenAI authentication server-side
|
| 170 |
+
|
| 171 |
+
## Usage
|
| 172 |
+
|
| 173 |
+
### Running the Application
|
| 174 |
+
|
| 175 |
+
```bash
|
| 176 |
+
# Using Streamlit directly
|
| 177 |
+
streamlit run src/voice_screening_ui/app.py
|
| 178 |
+
|
| 179 |
+
# Or via Docker (Streamlit service)
|
| 180 |
+
docker compose up voice_screening
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
#### troubleshootips tips
|
| 184 |
+
|
| 185 |
+
- if you see a warning on env variable not being set, pass the .env manually and rebuild on down (subsequent build will be faste due to docker layer caching)
|
| 186 |
+
``` bash
|
| 187 |
+
cd docker
|
| 188 |
+
docker-compose --env-file "../.env" up voice_screening -d --build
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
- run streamlit with python path set
|
| 192 |
+
``` bash
|
| 193 |
+
PYTHONPATH=. streamlit run src/voice_screening_ui/app.py
|
| 194 |
+
```
|
| 195 |
+
|
| 196 |
+
### User Flow
|
| 197 |
+
|
| 198 |
+
1. Start WebSocket proxy: `docker compose up websocket_proxy` (or run `python src/voice_screening_ui/proxy.py`)
|
| 199 |
+
2. Open Streamlit UI at `http://localhost:8502` (or configured port)
|
| 200 |
+
3. **Authentication:**
|
| 201 |
+
- Enter your email address
|
| 202 |
+
- Click "Request Code" to get authentication code
|
| 203 |
+
- Enter the code and click "Verify & Login"
|
| 204 |
+
4. Enter candidate email (optional for MVP)
|
| 205 |
+
5. Click "Start Interview"
|
| 206 |
+
6. Browser requests microphone permission
|
| 207 |
+
7. WebSocket connects to proxy with session token (proxy connects to OpenAI Realtime API)
|
| 208 |
+
8. Proxy configures session and sends greeting
|
| 209 |
+
9. Agent greets candidate
|
| 210 |
+
10. User clicks mic button to start recording
|
| 211 |
+
11. User speaks, audio streams to OpenAI
|
| 212 |
+
12. Transcript appears in real-time
|
| 213 |
+
13. Agent responds with audio (played sequentially)
|
| 214 |
+
14. User clicks mic button again to stop and send
|
| 215 |
+
15. User clicks "End Interview"
|
| 216 |
+
16. Click "Analyze Interview" to get results
|
| 217 |
+
17. Optionally save results to database
|
| 218 |
+
18. Click "Logout" to end session
|
| 219 |
+
|
| 220 |
+
## Technical Details
|
| 221 |
+
|
| 222 |
+
### OpenAI Realtime API
|
| 223 |
+
|
| 224 |
+
**WebSocket Connection:**
|
| 225 |
+
- Model: `gpt-realtime-mini`
|
| 226 |
+
- URL: `wss://api.openai.com/v1/realtime?model=gpt-realtime-mini`
|
| 227 |
+
- Headers: `Authorization: Bearer {API_KEY}`, `OpenAI-Beta: realtime=v1`
|
| 228 |
+
- Format: PCM16 audio at 24kHz, JSON messages
|
| 229 |
+
- Turn Detection: Server-side VAD with 10s silence duration (prevents auto-commit during recording)
|
| 230 |
+
|
| 231 |
+
**Key Message Types:**
|
| 232 |
+
- `session.update`: Configure session (modalities, voice, instructions)
|
| 233 |
+
- `input_audio_buffer.append`: Send audio chunks
|
| 234 |
+
- `input_audio_buffer.commit`: Commit audio for processing
|
| 235 |
+
- `response.audio_transcript.done`: Receive transcriptions
|
| 236 |
+
- `response.audio.delta`: Receive TTS audio chunks
|
| 237 |
+
- `response.text.done`: Receive text responses
|
| 238 |
+
|
| 239 |
+
### Audio Processing
|
| 240 |
+
|
| 241 |
+
**Recording:**
|
| 242 |
+
- Uses browser `ScriptProcessor` API (deprecated but functional)
|
| 243 |
+
- Captures audio at browser sample rate (typically 44.1kHz or 48kHz)
|
| 244 |
+
- Converts to PCM16 format
|
| 245 |
+
- Encodes to base64 for WebSocket transmission
|
| 246 |
+
- Streams chunks via `input_audio_buffer.append`
|
| 247 |
+
- Commits buffer via `input_audio_buffer.commit` when recording stops
|
| 248 |
+
|
| 249 |
+
**Playback:**
|
| 250 |
+
- Receives base64 PCM16 audio at 24kHz
|
| 251 |
+
- Decodes using `DataView` for proper byte order (little-endian)
|
| 252 |
+
- Converts PCM16 to Float32Array
|
| 253 |
+
- Resamples from 24kHz to browser sample rate using `OfflineAudioContext`
|
| 254 |
+
- Queues chunks for sequential playback (prevents overlapping audio)
|
| 255 |
+
- Plays through browser audio context
|
| 256 |
+
|
| 257 |
+
## Simplifications from Original Design
|
| 258 |
+
|
| 259 |
+
**Removed:**
|
| 260 |
+
- LangGraph agent complexity
|
| 261 |
+
- Twilio telephony integration
|
| 262 |
+
- FastAPI server
|
| 263 |
+
- Media Streams handling
|
| 264 |
+
- Complex state management
|
| 265 |
+
- Supervisor agent integration
|
| 266 |
+
|
| 267 |
+
**Kept:**
|
| 268 |
+
- Database models and utilities
|
| 269 |
+
- Analysis logic (simplified)
|
| 270 |
+
- Streamlit UI pattern
|
| 271 |
+
- OpenAI Realtime API integration
|
| 272 |
+
|
| 273 |
+
## File Structure
|
| 274 |
+
|
| 275 |
+
```
|
| 276 |
+
src/voice_screening_ui/
|
| 277 |
+
├── app.py # Main Streamlit UI (with authentication screen)
|
| 278 |
+
├── proxy.py # WebSocket proxy with auth endpoints and session management
|
| 279 |
+
├── analysis.py # Simple analysis function
|
| 280 |
+
├── components/
|
| 281 |
+
│ ├── voice_interface.html # HTML/JS for WebSocket and audio (no API key handling)
|
| 282 |
+
│ └── __init__.py
|
| 283 |
+
└── utils/
|
| 284 |
+
├── db.py # Database utilities
|
| 285 |
+
└── __init__.py
|
| 286 |
+
```
|
| 287 |
+
|
| 288 |
+
## Testing
|
| 289 |
+
|
| 290 |
+
**Manual Testing:**
|
| 291 |
+
1. Start Streamlit app (tested and works)
|
| 292 |
+
2. Test WebSocket connection (tested and works)
|
| 293 |
+
3. Test microphone access (tested and works)
|
| 294 |
+
4. Test audio recording and playback (tested and works)
|
| 295 |
+
5. Test transcript display (not tested)
|
| 296 |
+
6. Test analysis function (doesn't work, need work, a lot of work)
|
| 297 |
+
7. Test database saving (doesn't work, need work, a lot, a lot of work)
|
| 298 |
+
|
| 299 |
+
### Verification Script
|
| 300 |
+
To verify the integration of the voice screener with the candidate database and static questions, you can run the provided verification script.
|
| 301 |
+
|
| 302 |
+
**Option 1: Run via Docker (Recommended)**
|
| 303 |
+
This uses the containerized environment which already has all dependencies and network access to the database.
|
| 304 |
+
```bash
|
| 305 |
+
docker compose -f docker/docker-compose.yml run --rm -e POSTGRES_HOST=db websocket_proxy python tests/verify_voice_integration.py
|
| 306 |
+
```
|
| 307 |
+
|
| 308 |
+
**Option 2: Run Locally**
|
| 309 |
+
If you prefer to run it locally, you need to install the database requirements first:
|
| 310 |
+
```bash
|
| 311 |
+
pip install -r requirements/db.txt
|
| 312 |
+
python tests/verify_voice_integration.py
|
| 313 |
+
```
|
| 314 |
+
|
| 315 |
+
**Known Limitations:**
|
| 316 |
+
- Uses deprecated `ScriptProcessor` API (should migrate to `AudioWorklet`)
|
| 317 |
+
- Authentication codes displayed directly in MVP (should be sent via email/SMS in production)
|
| 318 |
+
- Session tokens stored in-memory (should use Redis/database in production)
|
| 319 |
+
- Simple error handling
|
| 320 |
+
- Limited session management
|
| 321 |
+
- Audio resampling may introduce slight latency
|
| 322 |
+
|
| 323 |
+
## Future Enhancements
|
| 324 |
+
|
| 325 |
+
- Migrate from `ScriptProcessor` to `AudioWorklet` API
|
| 326 |
+
- Send authentication codes via email/SMS (instead of displaying directly)
|
| 327 |
+
- Use Redis or database for session token storage (instead of in-memory)
|
| 328 |
+
- Add session persistence across page refreshes
|
| 329 |
+
- Improve error handling and reconnection logic
|
| 330 |
+
- Add recording playback
|
| 331 |
+
- Add interview question templates
|
| 332 |
+
- Optimize audio resampling performance
|
| 333 |
+
- Add audio level visualization
|
| 334 |
+
- Add rate limiting for authentication endpoints
|
| 335 |
+
- Add session refresh mechanism
|
| 336 |
+
- Integrate with supervisor agent (if needed)
|
docs/context_engineering/ideas.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ***Context Engineering***
|
| 2 |
+
---
|
| 3 |
+
|
| 4 |
+
This file serves as inspiration for context engneering techniques that are currently being used by experts in the industry.
|
| 5 |
+
|
| 6 |
+
***Inpsired by:***
|
| 7 |
+
- [Langchain](https://www.notion.so/Context-Engineering-for-Agents-2a1808527b17803ba221c2ced7eef508)
|
| 8 |
+
- [Video](https://www.youtube.com/watch?v=XFCkrYHHfpQ&t=217s)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
## ***`What is context engineering?`***
|
| 12 |
+
`The art and science to fill the context with just the right information for the next step.`
|
| 13 |
+
|
| 14 |
+
[Chroma research](https://research.trychroma.com/context-rot) shows that increasing input tokens daramatically impacts llm performance.
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
## Notes:
|
| 18 |
+
|
| 19 |
+
### 1) Tool calls
|
| 20 |
+
- tools calls bloat context as its added to messages list, with call itself + tool results
|
| 21 |
+
- hence flush or summarise tools to reduce context length and keep context dense with just imoprtant info
|
| 22 |
+
- furthermore tools are suually injected at sytem prompt level which bloats context as well and leads to confusion of wghich tool to use.
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
### Context Offloading
|
| 26 |
+
...
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
### Context Compaction & Offloading
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
|
docs/entrypoint_patterns.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 📄 How CV Parsing & LLM Evaluation Are Triggered — Summary
|
| 2 |
+
|
| 3 |
+
Below is a clean overview of the three architectural patterns for triggering **CV parsing** and **LLM-based CV evaluation** inside an agentic HR pipeline.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## 🧩 End-to-End Flow
|
| 8 |
+
1. Candidate uploads CV
|
| 9 |
+
2. System stores candidate entry in DB
|
| 10 |
+
3. CV parser runs automatically
|
| 11 |
+
4. Parsed CV JSON is stored in DB
|
| 12 |
+
5. Orchestrator detects that parsing is done
|
| 13 |
+
6. Orchestrator triggers the CV Screening Agent
|
| 14 |
+
7. LLM evaluates CV and stores results
|
| 15 |
+
8. Pipeline continues (voice → scheduling → final decision)
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
```sql
|
| 19 |
+
[User (Streamlit UI)]
|
| 20 |
+
↓
|
| 21 |
+
Upload CV + metadata (HTTP POST)
|
| 22 |
+
↓
|
| 23 |
+
[Orchestrator API]
|
| 24 |
+
↓
|
| 25 |
+
Save CV file (local or cloud)
|
| 26 |
+
↓
|
| 27 |
+
Write candidate entry to DB
|
| 28 |
+
↓
|
| 29 |
+
Trigger parsing pipeline
|
| 30 |
+
↓
|
| 31 |
+
Update parsed_cv_json + status='parsed'
|
| 32 |
+
↓
|
| 33 |
+
Orchestrator runs CV Screening Agent
|
| 34 |
+
↓
|
| 35 |
+
Write results to DB + status='cv_screened'
|
| 36 |
+
↓
|
| 37 |
+
[Streamlit polls /api/status/<candidate_id>]
|
| 38 |
+
↓
|
| 39 |
+
Display updated status + scores
|
| 40 |
+
```
|
| 41 |
+
---
|
| 42 |
+
|
| 43 |
+
## 🧠 Pattern A — Orchestrator-Driven State Machine (Recommended)
|
| 44 |
+
|
| 45 |
+
The orchestrator continuously monitors the candidate’s status in the database and decides the next action based on that state.
|
| 46 |
+
|
| 47 |
+
**Flow:**
|
| 48 |
+
- After parsing finishes, the system sets `status = "parsed"`
|
| 49 |
+
- The orchestrator checks the state and sees that the next step is CV screening
|
| 50 |
+
- It triggers the CV Screening Agent
|
| 51 |
+
- Once evaluation completes, the system updates status to `status = "cv_screened"`
|
| 52 |
+
- The orchestrator then moves to the next stage (voice screening, etc.)
|
| 53 |
+
|
| 54 |
+
**Why this is the best choice:**
|
| 55 |
+
- Most “agentic” (planning + reasoning)
|
| 56 |
+
- Clean separation between deterministic parsing and cognitive reasoning
|
| 57 |
+
- Perfect fit for LangGraph orchestration
|
| 58 |
+
- Easy to visualize reasoning and workflow progress
|
| 59 |
+
- Ideal for hackathon judges (transparency + intentionality)
|
| 60 |
+
|
| 61 |
+
---
|
| 62 |
+
|
| 63 |
+
## 🧠 Pattern B — Event-Based Trigger (Webhook, Queue, Pub/Sub)
|
| 64 |
+
|
| 65 |
+
The parsing component emits an event like “cv_parsed” when finished.
|
| 66 |
+
A listener or orchestrator receives that event and immediately triggers the CV Screening Agent.
|
| 67 |
+
|
| 68 |
+
**Pros:**
|
| 69 |
+
- Scales well
|
| 70 |
+
- Good for microservice architectures
|
| 71 |
+
|
| 72 |
+
**Cons:**
|
| 73 |
+
- Less agentic
|
| 74 |
+
- Harder to show planning logic and state transitions
|
| 75 |
+
- More infrastructure complexity
|
| 76 |
+
|
| 77 |
+
---
|
| 78 |
+
|
| 79 |
+
## 🧠 Pattern C — Orchestrator Polling the Database
|
| 80 |
+
|
| 81 |
+
A loop runs every few seconds, searching for candidates whose status is “parsed” and triggering CV evaluation when found.
|
| 82 |
+
|
| 83 |
+
**Pros:**
|
| 84 |
+
- Very simple to implement
|
| 85 |
+
- Works well for demos and prototypes
|
| 86 |
+
|
| 87 |
+
**Cons:**
|
| 88 |
+
- Not reactive
|
| 89 |
+
- Less elegant
|
| 90 |
+
- Not as agentic or clean as Pattern A
|
| 91 |
+
|
| 92 |
+
---
|
| 93 |
+
|
| 94 |
+
## 🏆 Recommendation
|
| 95 |
+
|
| 96 |
+
Use **Pattern A (Orchestrator-Driven State Machine)** for the hackathon submission.
|
| 97 |
+
|
| 98 |
+
**Benefits:**
|
| 99 |
+
- Natural agentic behavior
|
| 100 |
+
- Works directly with LangGraph’s planning style
|
| 101 |
+
- Provides clear reasoning transparency
|
| 102 |
+
- Fits well with your multi-agent architecture
|
| 103 |
+
- Easy to show on the Gradio dashboard
|
| 104 |
+
- Minimal complexity while still highly principled
|
| 105 |
+
|
| 106 |
+
---
|
| 107 |
+
|
| 108 |
+
## 📝 TL;DR
|
| 109 |
+
|
| 110 |
+
- CV parsing should run automatically after upload
|
| 111 |
+
- Parsed data should be saved to the DB
|
| 112 |
+
- **LLM CV evaluation should NOT be triggered by upload**
|
| 113 |
+
- Instead, the **orchestrator detects the new state and triggers evaluation**
|
| 114 |
+
- Pattern A (state machine) is the cleanest and most agentic solution
|
docs/how_langgraph_works.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🧭 LangGraph Overview: Message Flow, Tool Execution & GPT-OSS Integration
|
| 2 |
+
LangGraph is a workflow engine for building agentic systems on top of LangChain.
|
| 3 |
+
It models the reasoning–action loop between models and tools using a transparent graph of nodes.
|
| 4 |
+
|
| 5 |
+
This document explains:
|
| 6 |
+
1. How LangGraph message flow works
|
| 7 |
+
2. How tools and tool calls are represented
|
| 8 |
+
3. How your custom GPT-OSS (OpenRouter) wrapper integrates via bind_tools()
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
## ⚙️ Core Concept
|
| 13 |
+
LangGraph passes a `state` object between nodes, usually defined like this:
|
| 14 |
+
```python
|
| 15 |
+
from typing import Annotated, List, TypedDict, Any
|
| 16 |
+
from langgraph.graph.message import add_messages
|
| 17 |
+
|
| 18 |
+
class State(TypedDict):
|
| 19 |
+
messages: Annotated[List[Any], add_messages]
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
The `messages` list holds the entire conversation: user inputs, model responses, and tool outputs.
|
| 23 |
+
Each node reads this list, adds new messages, and returns an updated state.
|
| 24 |
+
|
| 25 |
+
**LangGraph uses LangChain message objects:**
|
| 26 |
+
- 🧑💼 HumanMessage — from the user
|
| 27 |
+
- 🤖 AIMessage — from the model (may include tool_calls)
|
| 28 |
+
- 🧰 ToolMessage — from a tool
|
| 29 |
+
- ⚙️ SystemMessage — optional context
|
| 30 |
+
|
| 31 |
+
## 🧩 The Typical Agent Flow
|
| 32 |
+
```sql
|
| 33 |
+
HumanMessage ─► LLMNode ─► ToolNode ─► LLMNode ─► Final Answer
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
### 1️⃣ Human input
|
| 37 |
+
```python
|
| 38 |
+
input_state = {
|
| 39 |
+
"messages": [
|
| 40 |
+
HumanMessage(
|
| 41 |
+
content="Compute 8 * 12 using calculator tool"
|
| 42 |
+
)
|
| 43 |
+
]
|
| 44 |
+
}
|
| 45 |
+
```
|
| 46 |
+
LangGraph starts from `START` and passes this to the first node (the model).
|
| 47 |
+
|
| 48 |
+
### 2️⃣ Model response: tool call
|
| 49 |
+
Your model (`ChatOpenRouter` running GPT-OSS) examines the conversation and returns an `AIMessage`:
|
| 50 |
+
```json
|
| 51 |
+
{
|
| 52 |
+
"content": "",
|
| 53 |
+
"tool_calls": [
|
| 54 |
+
{
|
| 55 |
+
"id": "call_1",
|
| 56 |
+
"function": {"name": "calculator", "arguments": "{\"a\":8,\"b\":12,\"op\":\"mul\"}"}
|
| 57 |
+
}
|
| 58 |
+
],
|
| 59 |
+
"finish_reason": "tool_calls"
|
| 60 |
+
}
|
| 61 |
+
```
|
| 62 |
+
✅ LangGraph detects `.tool_calls` and automatically routes the next step to the ToolNode.
|
| 63 |
+
|
| 64 |
+
### 3️⃣ Tool execution
|
| 65 |
+
The ***ToolNode*** executes the requested tool and adds a `ToolMessage` to the state:
|
| 66 |
+
```python
|
| 67 |
+
ToolMessage(
|
| 68 |
+
content='96.0',
|
| 69 |
+
name='calculator',
|
| 70 |
+
tool_call_id='call_1'
|
| 71 |
+
)
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
### 4️⃣ LLM continuation
|
| 75 |
+
The LLM now sees:
|
| 76 |
+
```
|
| 77 |
+
[
|
| 78 |
+
HumanMessage(...),
|
| 79 |
+
AIMessage(..., tool_calls=[...]),
|
| 80 |
+
ToolMessage(name="calculator", content="96.0")
|
| 81 |
+
]
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
It generates a final summary message:
|
| 85 |
+
```text
|
| 86 |
+
"The result of 8 × 12 is **96**."
|
| 87 |
+
```
|
| 88 |
+
Since this new message has no further `tool_calls`, LangGraph ends the workflow.
|
| 89 |
+
|
| 90 |
+
## 🧠 Internal Message Logic
|
| 91 |
+
| Step | Message Type | Produced By | Purpose |
|
| 92 |
+
| ---- | -------------- | ----------- | ------------------- |
|
| 93 |
+
| 1 | `HumanMessage` | user | Input |
|
| 94 |
+
| 2 | `AIMessage` | model | Requests tool |
|
| 95 |
+
| 3 | `ToolMessage` | ToolNode | Returns tool output |
|
| 96 |
+
| 4 | `AIMessage` | model | Final answer |
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
LangGraph uses conditional edges to decide whether to continue looping:
|
| 100 |
+
```python
|
| 101 |
+
workflow.add_conditional_edges(
|
| 102 |
+
"agent",
|
| 103 |
+
lambda state: "tools" if state["messages"][-1].tool_calls else END
|
| 104 |
+
)
|
| 105 |
+
```
|
| 106 |
+
This keeps running until no more tool calls are made.
|
| 107 |
+
|
| 108 |
+
## ⚙️ Example Graph Definition
|
| 109 |
+
```python
|
| 110 |
+
from langgraph.graph import StateGraph, START, END
|
| 111 |
+
from langgraph.prebuilt import ToolNode
|
| 112 |
+
from langchain_core.messages import HumanMessage
|
| 113 |
+
from langchain_core.tools import tool
|
| 114 |
+
from src.core.llm_providers.openrouter_llm import ChatOpenRouter
|
| 115 |
+
|
| 116 |
+
@tool
|
| 117 |
+
def calculator(a: float, b: float, op: str) -> float:
|
| 118 |
+
"""Perform a basic arithmetic operation."""
|
| 119 |
+
if op == "add": return a + b
|
| 120 |
+
if op == "sub": return a - b
|
| 121 |
+
if op == "mul": return a * b
|
| 122 |
+
if op == "div": return a / b
|
| 123 |
+
|
| 124 |
+
tools = [calculator]
|
| 125 |
+
|
| 126 |
+
# Initialize GPT-OSS LLM
|
| 127 |
+
llm = ChatOpenRouter(model_name="openai/gpt-oss-120b", temperature=0.0)
|
| 128 |
+
|
| 129 |
+
# ✅ Bind tools — this injects tool schemas into the model's context
|
| 130 |
+
llm_with_tools = llm.bind_tools(tools)
|
| 131 |
+
|
| 132 |
+
def call_model(state: State) -> State:
|
| 133 |
+
response = llm_with_tools.invoke(state["messages"])
|
| 134 |
+
return {"messages": [response]}
|
| 135 |
+
|
| 136 |
+
workflow = StateGraph(State)
|
| 137 |
+
workflow.add_node("agent", call_model)
|
| 138 |
+
workflow.add_node("tools", ToolNode(tools))
|
| 139 |
+
workflow.add_edge(START, "agent")
|
| 140 |
+
workflow.add_conditional_edges(
|
| 141 |
+
"agent", lambda s: "tools" if s["messages"][-1].tool_calls else END
|
| 142 |
+
)
|
| 143 |
+
workflow.add_edge("tools", "agent")
|
| 144 |
+
|
| 145 |
+
agent = workflow.compile()
|
| 146 |
+
input_state = {"messages": [HumanMessage(content="Compute 8 * 12 using calculator tool")]}
|
| 147 |
+
print(agent.invoke(input_state))
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
***>>> Check `notebooks/playground.ipynb` to see it in action!***
|
| 151 |
+
|
| 152 |
+
```mermaid
|
| 153 |
+
graph TD;
|
| 154 |
+
__start__(<p>__start__</p>)
|
| 155 |
+
agent(agent)
|
| 156 |
+
tools(tools)
|
| 157 |
+
__end__(<p>__end__</p>)
|
| 158 |
+
__start__ --> agent;
|
| 159 |
+
agent -->|tool_calls| tools;
|
| 160 |
+
tools --> agent;
|
| 161 |
+
agent -->|no tool_calls| __end__;
|
| 162 |
+
classDef default fill:#f2f0ff,line-height:1.2
|
| 163 |
+
classDef first fill-opacity:0
|
| 164 |
+
classDef last fill:#bfb6fc
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
## 🧩 How bind_tools() Works Internally
|
| 168 |
+
`bind_tools()` is the bridge between LangGraph and your LLM.
|
| 169 |
+
When you call:
|
| 170 |
+
|
| 171 |
+
```python
|
| 172 |
+
llm_with_tools = llm.bind_tools(tools)
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
LangChain:
|
| 176 |
+
1. Extracts each tool's name, description, and argument schema.
|
| 177 |
+
2. Converts them into an OpenAI function-calling schema JSON block (like `tools=[{"type":"function","function":{"name":...}}]`).
|
| 178 |
+
3. Attaches that schema to the model's context before each inference call.
|
| 179 |
+
So GPT-OSS sees an augmented prompt like this:
|
| 180 |
+
> “You have access to the following tools:
|
| 181 |
+
> calculator(a: float, b: float, op: str) — Perform a basic arithmetic operation.”
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
During inference, the model can reason (internally) about which tool to call and output structured JSON like:
|
| 185 |
+
```json
|
| 186 |
+
{
|
| 187 |
+
"tool_calls": [{
|
| 188 |
+
"function": {
|
| 189 |
+
"name": "calculator",
|
| 190 |
+
"arguments": "{\"a\":8,\"b\":12,\"op\":\"mul\"}"
|
| 191 |
+
}
|
| 192 |
+
}]
|
| 193 |
+
}
|
| 194 |
+
```
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
---
|
| 198 |
+
|
| 199 |
+
## 🧱 GPT-OSS Integration via ChatOpenRouter
|
| 200 |
+
To connect GPT-OSS via OpenRouter, use your wrapper:
|
| 201 |
+
```python
|
| 202 |
+
# src/core/llm_providers/openrouter_llm.py
|
| 203 |
+
from langchain_openai import ChatOpenAI
|
| 204 |
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
| 205 |
+
|
| 206 |
+
class OpenRouterSettings(BaseSettings):
|
| 207 |
+
OPENROUTER_API_KEY: str
|
| 208 |
+
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
| 209 |
+
|
| 210 |
+
class ChatOpenRouter(ChatOpenAI):
|
| 211 |
+
"""OpenRouter wrapper for GPT-OSS and other open models."""
|
| 212 |
+
|
| 213 |
+
def __init__(
|
| 214 |
+
self,
|
| 215 |
+
model_name: str = "openai/gpt-oss-120b",
|
| 216 |
+
base_url: str = "https://openrouter.ai/api/v1",
|
| 217 |
+
temperature: float = 0.2,
|
| 218 |
+
**kwargs,
|
| 219 |
+
):
|
| 220 |
+
settings = OpenRouterSettings()
|
| 221 |
+
super().__init__(
|
| 222 |
+
model_name=model_name,
|
| 223 |
+
openai_api_base=base_url,
|
| 224 |
+
openai_api_key=settings.OPENROUTER_API_KEY,
|
| 225 |
+
temperature=temperature,
|
| 226 |
+
**kwargs,
|
| 227 |
+
)
|
| 228 |
+
```
|
| 229 |
+
Then simply:
|
| 230 |
+
```python
|
| 231 |
+
llm = ChatOpenRouter()
|
| 232 |
+
llm_with_tools = llm.bind_tools(tools)
|
| 233 |
+
```
|
| 234 |
+
✅ This passes your validated API key to the OpenRouter endpoint.
|
| 235 |
+
|
| 236 |
+
✅ bind_tools() adds your tool schemas to the model input so GPT-OSS knows which functions exist.
|
| 237 |
+
|
| 238 |
+
✅ LangGraph handles execution and looping automatically.
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
---
|
| 242 |
+
|
| 243 |
+
## 🔁 Full Flow Recap
|
| 244 |
+
|
| 245 |
+
| Step | Component | What Happens |
|
| 246 |
+
| ---- | ---------------------- | ---------------------------------------------------------- |
|
| 247 |
+
| 1 | `ChatOpenRouter` | Sends messages to GPT-OSS via OpenRouter API |
|
| 248 |
+
| 2 | `bind_tools()` | Injects tool schema into model context |
|
| 249 |
+
| 3 | `AIMessage.tool_calls` | Model outputs structured tool call |
|
| 250 |
+
| 4 | `ToolNode` | Executes the requested function |
|
| 251 |
+
| 5 | `ToolMessage` | Returns tool result to model |
|
| 252 |
+
| 6 | `AIMessage` | Model produces natural-language final answer |
|
| 253 |
+
| ✅ | LangGraph | Orchestrates routing and maintains full conversation state |
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
## ✅ TL;DR
|
| 257 |
+
- LangGraph represents an agent loop as a message-passing graph.
|
| 258 |
+
- Messages include `HumanMessage`, `AIMessage`, and `ToolMessage`.
|
| 259 |
+
- `bind_tools()` injects your tool schemas into the LLM's context so it can call them.
|
| 260 |
+
- The ToolNode executes the functions and feeds results back into the loop.
|
| 261 |
+
- Your `ChatOpenRouter` wrapper lets GPT-OSS models participate in this system seamlessly.
|
| 262 |
+
|
docs/mcp/gmail_mcp_gcp_setup.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ***`Gmail MCP – GCP Setup (Terraform + Bash)`***
|
| 2 |
+
|
| 3 |
+
This folder provisions the minimum GCP infrastructure to run the [Gmail MCP server](https://github.com/theposch/gmail-mcp/tree/main) without requiring billing:
|
| 4 |
+
- Creates (or adopts) a ***GCP project***
|
| 5 |
+
- Enables ***gmail.googleapis.com***
|
| 6 |
+
- Grants your user:
|
| 7 |
+
- `roles/editor`
|
| 8 |
+
- `roles/serviceusage.serviceUsageAdmin`
|
| 9 |
+
Prints console links to finish OAuth (consent screen + Desktop client)
|
| 10 |
+
> Billing is **not required** for Gmail API or OAuth Desktop client.
|
| 11 |
+
|
| 12 |
+
## ***Prerequisites***
|
| 13 |
+
- **Terraform** ≥ 1.6
|
| 14 |
+
- **gcloud** (optional but useful for verifying/importing projects)
|
| 15 |
+
- A Google account (you'll also add it as a **Test user** on the OAuth consent screen)
|
| 16 |
+
|
| 17 |
+
## Files
|
| 18 |
+
- `versions.tf` – provider & Terraform version pins
|
| 19 |
+
- `providers.tf` – Google provider config (uses project_id and region)
|
| 20 |
+
- `variables.tf` – input variables
|
| 21 |
+
- `main.tf` – project, Gmail API enablement, IAM bindings
|
| 22 |
+
- `outputs.tf` – project IDs and console URLs
|
| 23 |
+
- `terraform.tfvars` – team defaults (simple `key = "value"` pairs)
|
| 24 |
+
|
| 25 |
+
Example `terraform.tfvars`:
|
| 26 |
+
```python
|
| 27 |
+
project_id = "gradio-hackathon-25"
|
| 28 |
+
project_name = "Gradio Agent MCP Hackathon 25"
|
| 29 |
+
user_email = "hr.cjordan.agent.hack.winter25@gmail.com"
|
| 30 |
+
# region = "europe-west3" # optional
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
## ***Quick Start (recommended: use the scripts)**
|
| 34 |
+
From the ***repo root***:
|
| 35 |
+
1. **Authenticate gcloud + ADC**
|
| 36 |
+
```bash
|
| 37 |
+
chmod +x scripts/gcp_setup.sh
|
| 38 |
+
./scripts/gcp_setup.sh
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
2. **Apply Terraform with smart defaults + auto-import**
|
| 42 |
+
```bash
|
| 43 |
+
chmod +x scripts/terraform_apply.sh
|
| 44 |
+
./scripts/terraform_apply.sh
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
- The script **prompts** for `project_id`, `project_name`, `user_email`.
|
| 48 |
+
- Press ***Enter*** to use defaults from `terraform/terraform.tfvars`.
|
| 49 |
+
- If the project already exists, it is **auto-imported** to avoid `409 alreadyExists`.
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
+
## ***Manual Usage (alternative)***
|
| 54 |
+
Run these from this `terraform/` directory:
|
| 55 |
+
```bash
|
| 56 |
+
terraform init
|
| 57 |
+
|
| 58 |
+
# If the project already exists, import it so Terraform manages it:
|
| 59 |
+
# terraform import google_project.project <your-project-id>
|
| 60 |
+
|
| 61 |
+
terraform apply
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
Override values:
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
terraform apply \
|
| 68 |
+
-var="project_id=my-mcp-project" \
|
| 69 |
+
-var="project_name=My MCP Project" \
|
| 70 |
+
-var="user_email=you@example.com"
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
Or via env vars:
|
| 74 |
+
```bash
|
| 75 |
+
export TF_VAR_project_id="my-mcp-project"
|
| 76 |
+
export TF_VAR_project_name="My MCP Project"
|
| 77 |
+
export TF_VAR_user_email="you@example.com"
|
| 78 |
+
terraform apply
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
## **Outputs**
|
| 82 |
+
- `project_id` / `project_number`
|
| 83 |
+
- `gmail_api_service` — `"gmail.googleapis.com"` (resource present ⇒ enabled)
|
| 84 |
+
- `console_oauth_consent_screen_url` — configure consent (External, Test user, add scope)
|
| 85 |
+
- `console_oauth_credentials_url` — create ***OAuth 2.0 Client ID*** (Desktop app)
|
| 86 |
+
|
| 87 |
+
## **Final OAuth Setup (one-time, in Console)**
|
| 88 |
+
|
| 89 |
+
Terraform cannot create the **OAuth consent screen** or **Desktop OAuth client**, so you'll do these two steps once in the Google Cloud Console.
|
| 90 |
+
This setup allows your **local Gmail MCP server** to access Gmail via OAuth securely.
|
| 91 |
+
|
| 92 |
+
> 💡 **Tip:** In the new Google Cloud UI, the old “Scopes” and “Test users” tabs are now under **Data access** and **Audience** in the left sidebar.
|
| 93 |
+
|
| 94 |
+
---
|
| 95 |
+
|
| 96 |
+
### 1️⃣ **Configure the OAuth Consent Screen**
|
| 97 |
+
|
| 98 |
+
**Purpose:** Identify your app to Google and specify who can use it during testing.
|
| 99 |
+
|
| 100 |
+
1. Open the link printed in Terraform outputs:
|
| 101 |
+
→ `console_oauth_consent_screen_url`
|
| 102 |
+
|
| 103 |
+
2. If prompted, choose **User type: External**, then click **Create**.
|
| 104 |
+
|
| 105 |
+
3. Fill out **App info**:
|
| 106 |
+
- **App name:** `Gmail MCP Local`
|
| 107 |
+
- **User support email:** your Gmail address
|
| 108 |
+
- **Developer contact email:** your Gmail address
|
| 109 |
+
- Click **Save and Continue**
|
| 110 |
+
|
| 111 |
+
4. In the left sidebar, go to **Data access**
|
| 112 |
+
- Click **Add or remove scopes**
|
| 113 |
+
- Add the following scopes:
|
| 114 |
+
```text
|
| 115 |
+
https://www.googleapis.com/auth/gmail.modify
|
| 116 |
+
openid
|
| 117 |
+
https://www.googleapis.com/auth/userinfo.email
|
| 118 |
+
```
|
| 119 |
+
✅ *These provide read, send, and modify access — no extra Gmail scopes required.*
|
| 120 |
+
|
| 121 |
+
5. Go to **Audience** (left sidebar)
|
| 122 |
+
- Under **Test users**, click **Add users**
|
| 123 |
+
- Add your Gmail account address
|
| 124 |
+
- Click **Save**
|
| 125 |
+
|
| 126 |
+
6. Go to **Summary** and confirm:
|
| 127 |
+
- User type → **External**
|
| 128 |
+
- Publishing status → **Testing**
|
| 129 |
+
- Test users → your Gmail account
|
| 130 |
+
- Scopes → shows Gmail modify, openid, userinfo.email
|
| 131 |
+
|
| 132 |
+
---
|
| 133 |
+
|
| 134 |
+
### 2️⃣ **Create a Desktop OAuth Client**
|
| 135 |
+
|
| 136 |
+
**Purpose:** This provides the credentials your **local MCP server** uses to initiate the OAuth flow.
|
| 137 |
+
|
| 138 |
+
1. Open the second Terraform output link:
|
| 139 |
+
→ `console_oauth_credentials_url`
|
| 140 |
+
|
| 141 |
+
2. Click **Create credentials → OAuth client ID**
|
| 142 |
+
|
| 143 |
+
3. Choose:
|
| 144 |
+
- **Application type:** `Desktop app`
|
| 145 |
+
- **Name:** `gmail-mcp-desktop`
|
| 146 |
+
|
| 147 |
+
4. Click **Create**, then **Download JSON**, and move it into the expected directory:
|
| 148 |
+
```bash
|
| 149 |
+
mkdir -p ~/.gmail-mcp
|
| 150 |
+
mv ~/Downloads/client_secret_*.json ~/.gmail-mcp/credentials.json
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
### ⚠️ ***Important: Two Different JSONs***
|
| 154 |
+
|
| 155 |
+
- The file from:
|
| 156 |
+
```bash
|
| 157 |
+
gcloud auth application-default login
|
| 158 |
+
```
|
| 159 |
+
is your ***Application Default Credentials (ADC)*** — used by Terraform and `gcloud`.
|
| 160 |
+
It is ***not*** the same as the Desktop OAuth client JSON.
|
| 161 |
+
The Gmail MCP server requires the ***Desktop OAuth client JSON*** you downloaded.
|
| 162 |
+
→ Place it at `~/.gmail-mcp/credentials.json`.
|
| 163 |
+
|
| 164 |
+
### 🚀 ***When You Run the MCP Server***
|
| 165 |
+
The server will open a browser window asking you to sign in and approve access.
|
| 166 |
+
You'll see your app name (`Gmail MCP Local`) and the Gmail modify scope.
|
| 167 |
+
After approving, tokens are cached locally (usually `~/.gmail-mcp/token.json`), so you won't need to approve again.
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
---
|
| 171 |
+
|
| 172 |
+
### ⚙️ **Install Required Tools**
|
| 173 |
+
|
| 174 |
+
Before testing or running the Gmail MCP server, make sure the following tools are installed:
|
| 175 |
+
|
| 176 |
+
#### 🟣 1. Install `uv`
|
| 177 |
+
`uv` is a fast Python package manager used to run the Gmail MCP server.
|
| 178 |
+
|
| 179 |
+
Check if it's already installed:
|
| 180 |
+
```bash
|
| 181 |
+
uv --version
|
| 182 |
+
```
|
| 183 |
+
If not, install it:
|
| 184 |
+
|
| 185 |
+
```bash
|
| 186 |
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
Verify:
|
| 190 |
+
```
|
| 191 |
+
uv --version
|
| 192 |
+
```
|
| 193 |
+
#### 🟠 2. Install Node.js (includes `npm` and `npx`)
|
| 194 |
+
npx is used to run the MCP Inspector, which lets you test your Gmail MCP server locally.
|
| 195 |
+
```bash
|
| 196 |
+
brew install node
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
### 🧪 ***Testing Locally with MCP Inspector***
|
| 200 |
+
Once toBefore connecting to Claude Desktop or a LangGraph agent, you can visually inspect and test your Gmail MCP server using the ***MCP Inspector*** web UI.
|
| 201 |
+
|
| 202 |
+
#### 1️⃣ **Run with simple path (direct server entry)**
|
| 203 |
+
Use this if you're already inside the ``gmail-mcp directory or the script path resolves cleanly:
|
| 204 |
+
```bash
|
| 205 |
+
npx @modelcontextprotocol/inspector uv run /Users/sebastianwefers/Desktop/development/recruitment-agent-mcp-hackathon-winter25/src/mcp_servers/gmail-mcp/src/gmail/server.py \
|
| 206 |
+
--creds-file-path ~/.gmail-mcp/credentials.json \
|
| 207 |
+
--token-path ~/.gmail-mcp/token.json
|
| 208 |
+
```
|
| 209 |
+
|
| 210 |
+
#### 2️⃣ **Run with full project context (recommended)**
|
| 211 |
+
This variant is more robust and works regardless of your working directory, because it explicitly tells `uv` which project directory to use and where your binaries are.
|
| 212 |
+
|
| 213 |
+
```bash
|
| 214 |
+
npx @modelcontextprotocol/inspector \
|
| 215 |
+
/Users/sebastianwefers/.local/bin/uv \
|
| 216 |
+
--directory /Users/sebastianwefers/Desktop/development/recruitment-agent-mcp-hackathon-winter25/src/mcp_servers/gmail-mcp \
|
| 217 |
+
run gmail \
|
| 218 |
+
--creds-file-path ~/.gmail-mcp/credentials.json \
|
| 219 |
+
--token-path ~/.gmail-mcp/token.json
|
| 220 |
+
```
|
| 221 |
+
|
| 222 |
+
#### 🔍 What Happens
|
| 223 |
+
When you run either command, you should see output similar to:
|
| 224 |
+
```bash
|
| 225 |
+
Starting MCP inspector...
|
| 226 |
+
⚙️ Proxy server listening on localhost:6277
|
| 227 |
+
🔑 Session token: 8498939effc01e03c1b879efa72768e45608056ef1ad45e5c80344a7d9362a72
|
| 228 |
+
Use this token to authenticate requests or set DANGEROUSLY_OMIT_AUTH=true to disable auth
|
| 229 |
+
|
| 230 |
+
🚀 MCP Inspector is up and running at:
|
| 231 |
+
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=8498939effc01e03c1b879efa72768e45608056ef1ad45e5c80344a7d9362a72
|
| 232 |
+
|
| 233 |
+
🌐 Opening browser...
|
| 234 |
+
|
| 235 |
+
```
|
| 236 |
+
|
| 237 |
+
This automatically opens a local browser window to the MCP Inspector UI, connected to your Gmail MCP server.
|
| 238 |
+
|
| 239 |
+
✅ Expected behavior:
|
| 240 |
+
- On first run, a browser window will prompt you to log in and approve access.
|
| 241 |
+
- After successful OAuth, a token file will be created:
|
| 242 |
+
```bash
|
| 243 |
+
~/.gmail-mcp/token.json
|
| 244 |
+
```
|
| 245 |
+
- Subsequent runs reuse this token — no re-auth required.
|
| 246 |
+
- You can now explore, invoke, and inspect your Gmail MCP tools visually (e.g., `listEmails`, `sendEmail`, `modifyLabel`, etc.) right from the web UI.
|
| 247 |
+
|
| 248 |
+
### 💻 ***Connecting the Gmail MCP Server to Claude Desktop***
|
| 249 |
+
1. Open your Claude Desktop configuration file:
|
| 250 |
+
```bash
|
| 251 |
+
nano ~/Library/Application Support/Claude/claude_desktop_config.json
|
| 252 |
+
```
|
| 253 |
+
2. Add this block (update paths if necessary):
|
| 254 |
+
```json
|
| 255 |
+
{
|
| 256 |
+
"mcpServers": {
|
| 257 |
+
"gmail": {
|
| 258 |
+
"command": "uv",
|
| 259 |
+
"args": [
|
| 260 |
+
"--directory",
|
| 261 |
+
"/Users/sebastianwefers/Desktop/development/recruitment-agent-mcp-hackathon-winter25/src/mcp_servers/gmail-mcp",
|
| 262 |
+
"run",
|
| 263 |
+
"gmail",
|
| 264 |
+
"--creds-file-path",
|
| 265 |
+
"/Users/sebastianwefers/.gmail-mcp/credentials.json",
|
| 266 |
+
"--token-path",
|
| 267 |
+
"/Users/sebastianwefers/.gmail-mcp/token.json"
|
| 268 |
+
]
|
| 269 |
+
}
|
| 270 |
+
}
|
| 271 |
+
}
|
| 272 |
+
```
|
| 273 |
+
3. Save the file and restart Claude Desktop.
|
| 274 |
+
|
| 275 |
+
4. Open ***Settings → Model Context Protocol → Add Server***, then connect to gmail.
|
| 276 |
+
|
| 277 |
+
Claude will now be able to:
|
| 278 |
+
- 📥 Read emails
|
| 279 |
+
- ✉️ Compose drafts
|
| 280 |
+
- 🏷 Send and modify Gmail messages directly from your account.
|
| 281 |
+
|
| 282 |
+
— all directly via your Gmail MCP server.
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
## 🧩 Why Deleting token.json Fixes the “invalid_grant” Error
|
| 286 |
+
The error occurs because the stored refresh token in token.json is expired or revoked, so Google rejects all refresh attempts.
|
| 287 |
+
Deleting the file forces the app to start a new OAuth flow, prompting you to log in again and generating a new, valid refresh token — which restores access to the Gmail API.
|
| 288 |
+
|
| 289 |
+
```bash
|
| 290 |
+
# 1. Remove the invalid cached token
|
| 291 |
+
rm /Users/sebastianwefers/Desktop/projects/recruitment-agent-mcp-hackathon-winter25/secrets/gmail-mcp/token.json
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
# 2. Re-run the Gmail MCP server (which triggers OAuth again)
|
| 295 |
+
python -m src.mcp_servers.gmail_mcp
|
| 296 |
+
```
|
| 297 |
+
|
| 298 |
+
Then, when the script printed a Google sign-in URL, you:
|
| 299 |
+
1. Opened it in your browser,
|
| 300 |
+
2. Logged in to your Google account,
|
| 301 |
+
3. Approved the Gmail API access,
|
| 302 |
+
4. And the new valid token.json was automatically recreated at:
|
| 303 |
+
```bash
|
| 304 |
+
~/.gmail-mcp/token.json
|
| 305 |
+
```
|
docs/mcp/google_tools_mcp.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Google Calendar and GMail Tools / MCP
|
| 2 |
+
|
| 3 |
+
## 1) ***`Base setup`***
|
| 4 |
+
### 1.1) ***GMail Account***
|
| 5 |
+
|
| 6 |
+
### 1.2) ***Google Cloud***
|
| 7 |
+
|
| 8 |
+
#### Terraform Modifications (Minimal)
|
| 9 |
+
You can extend your existing Gmail Terraform to include Calendar support.
|
| 10 |
+
Add these to your `main.tf`:
|
| 11 |
+
```bash
|
| 12 |
+
# Enable the Google Calendar API
|
| 13 |
+
resource "google_project_service" "calendar_api" {
|
| 14 |
+
project = google_project.project.project_id
|
| 15 |
+
service = "calendar.googleapis.com"
|
| 16 |
+
disable_on_destroy = false
|
| 17 |
+
}
|
| 18 |
+
```
|
| 19 |
+
And if you want, you can add an output for convenience:
|
| 20 |
+
```bash
|
| 21 |
+
output "console_calendar_api_url" {
|
| 22 |
+
value = "https://console.cloud.google.com/apis/library/calendar.googleapis.com?project=${google_project.project.project_id}"
|
| 23 |
+
}
|
| 24 |
+
```
|
| 25 |
+
After adding, re-run your scripts:
|
| 26 |
+
```bash
|
| 27 |
+
cd terraform
|
| 28 |
+
terraform apply
|
| 29 |
+
```
|
| 30 |
+
This enables the Calendar API in the same project your Gmail MCP is using — so you don't have to create a second one.
|
| 31 |
+
|
| 32 |
+
Terraform will:
|
| 33 |
+
1. Detect that you already have a project and Gmail API from before.
|
| 34 |
+
2. Notice the new Calendar API resource in ``main.tf.
|
| 35 |
+
3. Apply only that new change (plus any small diff in IAM roles if needed).
|
| 36 |
+
|
| 37 |
+
💡 What Happens Internally
|
| 38 |
+
When you run `terraform apply`, Terraform will:
|
| 39 |
+
- Read your current state file (`terraform.tfstate`).
|
| 40 |
+
- Query GCP to check what's already deployed.
|
| 41 |
+
- Compute a plan (the difference between your state and the `.tf` files).
|
| 42 |
+
|
| 43 |
+
#### 🔑 OAuth Setup — Shared Consent Screen
|
| 44 |
+
You do not need a new consent screen — just reuse your existing one (Gmail MCP Local) and add the Calendar scope.
|
| 45 |
+
|
| 46 |
+
Go to:
|
| 47 |
+
👉 [Google Cloud Console → APIs & Services → OAuth consent screen → Edit app → Data access → Add scopes]
|
| 48 |
+
Add this scope:
|
| 49 |
+
```arduino
|
| 50 |
+
https://www.googleapis.com/auth/calendar
|
| 51 |
+
```
|
| 52 |
+
You'll now have Gmail + Calendar under one consent.
|
| 53 |
+
Then, create a ***second OAuth client***:
|
| 54 |
+
Application type: Desktop app
|
| 55 |
+
Name: `calendar-mcp-desktop`
|
| 56 |
+
Download the credentials JSON → save it to:
|
| 57 |
+
```bash
|
| 58 |
+
~/.calendar-mcp/credentials.json
|
| 59 |
+
```
|
| 60 |
+
The Calendar MCP server will then use this credentials file when it first authenticates.
|
| 61 |
+
|
| 62 |
+
#### 🧩 MCP Client Config (Claude or LangGraph)
|
| 63 |
+
Just add a new block alongside your Gmail entry.
|
| 64 |
+
For Claude Desktop (`claude_desktop_config.json`)
|
| 65 |
+
```json
|
| 66 |
+
{
|
| 67 |
+
"mcpServers": {
|
| 68 |
+
"gmail": {
|
| 69 |
+
"command": "uv",
|
| 70 |
+
"args": [
|
| 71 |
+
"--directory",
|
| 72 |
+
"/Users/sebastianwefers/Desktop/development/recruitment-agent-mcp-hackathon-winter25/src/mcp_servers/gmail-mcp",
|
| 73 |
+
"run",
|
| 74 |
+
"gmail",
|
| 75 |
+
"--creds-file-path",
|
| 76 |
+
"/Users/sebastianwefers/.gmail-mcp/credentials.json",
|
| 77 |
+
"--token-path",
|
| 78 |
+
"/Users/sebastianwefers/.gmail-mcp/token.json"
|
| 79 |
+
]
|
| 80 |
+
},
|
| 81 |
+
"google_calendar": {
|
| 82 |
+
"command": "uv",
|
| 83 |
+
"args": [
|
| 84 |
+
"--directory",
|
| 85 |
+
"/Users/sebastianwefers/Desktop/development/recruitment-agent-mcp-hackathon-winter25/src/mcp_servers/calendar-mcp",
|
| 86 |
+
"run",
|
| 87 |
+
"calendar"
|
| 88 |
+
]
|
| 89 |
+
}
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
For LangGraph:
|
| 95 |
+
```python
|
| 96 |
+
client = MultiServerMCPClient({
|
| 97 |
+
"gmail": {
|
| 98 |
+
"command": "uv",
|
| 99 |
+
"args": [
|
| 100 |
+
"--directory", "/Users/sebastianwefers/Desktop/development/recruitment-agent-mcp-hackathon-winter25/src/mcp_servers/gmail-mcp",
|
| 101 |
+
"run", "gmail",
|
| 102 |
+
"--creds-file-path", "/Users/sebastianwefers/.gmail-mcp/credentials.json",
|
| 103 |
+
"--token-path", "/Users/sebastianwefers/.gmail-mcp/token.json"
|
| 104 |
+
],
|
| 105 |
+
"transport": "stdio"
|
| 106 |
+
},
|
| 107 |
+
"google_calendar": {
|
| 108 |
+
"command": "uv",
|
| 109 |
+
"args": [
|
| 110 |
+
"--directory", "/Users/sebastianwefers/Desktop/development/recruitment-agent-mcp-hackathon-winter25/src/mcp_servers/calendar-mcp",
|
| 111 |
+
"run", "calendar"
|
| 112 |
+
],
|
| 113 |
+
"transport": "stdio"
|
| 114 |
+
}
|
| 115 |
+
})
|
| 116 |
+
```
|
| 117 |
+
#### Environment Variables (.env)
|
| 118 |
+
Create the .env in your calendar-mcp repo root, just like described in its README:
|
| 119 |
+
```bash
|
| 120 |
+
GOOGLE_CLIENT_ID='YOUR_CLIENT_ID'
|
| 121 |
+
GOOGLE_CLIENT_SECRET='YOUR_CLIENT_SECRET'
|
| 122 |
+
TOKEN_FILE_PATH='.gcp-saved-tokens.json'
|
| 123 |
+
OAUTH_CALLBACK_PORT=8080
|
| 124 |
+
CALENDAR_SCOPES='https://www.googleapis.com/auth/calendar'
|
| 125 |
+
```
|
| 126 |
+
⚠️ Make sure the redirect URI matches:
|
| 127 |
+
```bash
|
| 128 |
+
http://localhost:8080/oauth2callback
|
| 129 |
+
```
|
| 130 |
+
You'll go through one browser OAuth login on first run, and then `.gcp-saved-tokens.json` will be created — no need to repeat.
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
## 2) ***`Model Context Protocol`***
|
| 134 |
+
**References**
|
| 135 |
+
- [Official MCP Docs](https://modelcontextprotocol.io/docs/getting-started/intro)
|
| 136 |
+
- [MCP Crash Course by YouTuber & AI Engineer Dave Ebbelaar](https://www.youtube.com/watch?v=5xqFjh56AwM&t=761s)
|
| 137 |
+
- *Existing Repo's*
|
| 138 |
+
- [Curated list of MCP servers](https://github.com/modelcontextprotocol/servers) hosted by `MCP` themselves.
|
| 139 |
+
- [Goole Calendar](https://github.com/deciduus/calendar-mcp/blob/main/README.md)
|
| 140 |
+
- calendar repo alterntives:
|
| 141 |
+
- https://github.com/nspady/google-calendar-mcp/tree/main/src/tools
|
| 142 |
+
```psql
|
| 143 |
+
# Calendar MCP (Dual Layer)
|
| 144 |
+
LLM Agent
|
| 145 |
+
│
|
| 146 |
+
│ JSON-RPC over STDIO
|
| 147 |
+
▼
|
| 148 |
+
MCP Bridge (mcp_bridge.py)
|
| 149 |
+
│ HTTP requests to localhost:8000
|
| 150 |
+
▼
|
| 151 |
+
FastAPI Server (server.py)
|
| 152 |
+
│
|
| 153 |
+
└── Google Calendar API (OAuth + REST)
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
- [GMail](https://github.com/theposch/gmail-mcp/blob/main/README.md)
|
| 157 |
+
```psql
|
| 158 |
+
# Gmail MCP (Pure MCP)
|
| 159 |
+
LLM Agent
|
| 160 |
+
│
|
| 161 |
+
│ JSON-RPC over STDIO
|
| 162 |
+
▼
|
| 163 |
+
Gmail MCP Server
|
| 164 |
+
│
|
| 165 |
+
└── Gmail API (OAuth + REST)
|
| 166 |
+
```
|
| 167 |
+
- [Gmail](https://github.com/MCP-Mirror/Samarth2001_gmail-mcp)
|
| 168 |
+
- [Gmail](https://github.com/jasonsum/gmail-mcp-server)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
## 🧱 1️⃣ Compatibility Breakdown
|
| 172 |
+
| Area | Gmail MCP | Calendar MCP | Compatible? | Notes |
|
| 173 |
+
| ------------------- | ---------------------------------------------- | ----------------------------------------------- | ----------- | --------------------------------------------------------------------------- |
|
| 174 |
+
| **Transport** | MCP via STDIO | MCP via STDIO (through FastAPI bridge) | ✅ | Works out of the box with same client setup. |
|
| 175 |
+
| **Auth Type** | OAuth 2.0 Desktop Client | OAuth 2.0 Desktop Client | ✅ | Identical flow; can reuse same consent screen + test users. |
|
| 176 |
+
| **Scopes** | `https://www.googleapis.com/auth/gmail.modify` | `https://www.googleapis.com/auth/calendar` | ✅ | Different scopes, but both can live under one consent screen. |
|
| 177 |
+
| **Terraform** | Creates project, enables Gmail API, sets roles | Just needs Calendar API enabled too | ✅ | Add one more API + scope to Terraform config. |
|
| 178 |
+
| **Token Storage** | `~/.gmail-mcp/token.json` | `.gcp-saved-tokens.json` | ✅ | Each uses its own token file; keep separate to avoid refresh token mix-ups. |
|
| 179 |
+
| **Runtime** | `uv` stdio server | `python run_server.py` (auto-switches to stdio) | ✅ | You can use `uv` for both, if you prefer consistency. |
|
| 180 |
+
| **MCP Integration** | Claude / LangGraph via config | Same | ✅ | Just add another entry under `mcpServers`. |
|
intro.md
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ***`Gradio Agents & MCP Hackathon Winter Edition 2025`***
|
| 2 |
+
|
| 3 |
+
## 🏁 Overview
|
| 4 |
+
This repository hosts our team's submission for **Track 2: MCP in Action** in the [MCP's 1st Birthday Hackathon](https://huggingface.co/MCP-1st-Birthday).
|
| 5 |
+
|
| 6 |
+
Our goal is to build an **autonomous agentic system** that demonstrates:
|
| 7 |
+
- **Planning, reasoning, and execution**
|
| 8 |
+
- Integration of **custom tools, MCP tools, or external APIs**
|
| 9 |
+
- Effective **context engineering**
|
| 10 |
+
- Clear, practical **user value**
|
| 11 |
+
|
| 12 |
+
We'll use **LangGraph** as our orchestration backbone for building multi-turn, tool-using, and context-aware agents.
|
| 13 |
+
|
| 14 |
+
> ***`Check hackathon README for detilaed requirements.`***
|
| 15 |
+
|
| 16 |
+
## 🧠 ***`Tools & Frameworks`***
|
| 17 |
+
|
| 18 |
+
- 🧩 [LangGraph](https://docs.langchain.com/oss/python/langgraph/overview): for multi-agent orchestration and planning
|
| 19 |
+
- Why & how they built [LangGraph for production agents](https://blog.langchain.com/building-langgraph/)
|
| 20 |
+
- 🧠 **LLM Engines:** [OpenAI](https://openai.com) / [Anthropic](https://www.anthropic.com) — reasoning and planning models
|
| 21 |
+
- gpt-oss inference providers
|
| 22 |
+
- [Open Router](https://openrouter.ai/openai/gpt-oss-20b):
|
| 23 |
+
- LangChain Wrapper: https://github.com/langchain-ai/langchain/discussions/27964
|
| 24 |
+
- [TogetherAI](https://www.together.ai/openai)
|
| 25 |
+
- 💬 [Gradio](https://www.gradio.app/): for the UI and context-engineering demos
|
| 26 |
+
- ⚙️ [MCP](https://modelcontextprotocol.io/docs/getting-started/intro) Tools: standardized interfaces for Gmail, Google Calendar, Voice technologies and other APIs
|
| 27 |
+
- ☁️ [Google Cloud Platform](https://cloud.google.com): optional backend for hosting MCP servers and integrated services
|
| 28 |
+
- 📞 [Twilio](https://www.twilio.com/en-us): enables automated voice calls and candidate interactions
|
| 29 |
+
- 🔊 [ElevenLabs](https://elevenlabs.io): (optional) natural text-to-speech for realistic voice screenings
|
| 30 |
+
- 🎙️ [Whisper-based Transcription API](https://whisperapi.com) (or [OpenAI Whisper API](https://platform.openai.com/docs/guides/speech-to-text) ) — for speech-to-text functionality in voice interviews
|
| 31 |
+
- 🧭 [Langfuse](https://langfuse.com) or [LangSmith](https://docs.langchain.com/langsmith/quick-start-studio): debugging, observability, and trace visualization
|
| 32 |
+
- 📄 [Docling](https://www.docling.ai): for parsing and analyzing uploaded CV documents
|
| 33 |
+
- 🧱 [Pydantic](https://docs.pydantic.dev/latest/): for structured outputs and data validation
|
| 34 |
+
- 🔀 [Parlant](https://github.com/emcie-co/parlant): enables agents to handle multi-intent, free-form conversations by dynamically activating relevant guidelines instead of rigidly routing to a single sub-agent — solving the context fragmentation problem inherent in traditional LangGraph supervisor patterns.
|
| 35 |
+
|
| 36 |
+
## 📚 ***`References for Context Engineering`***
|
| 37 |
+
|
| 38 |
+
- [**Context Engineering for AI Agents — Manus Blog**](https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus)
|
| 39 |
+
- [**YouTube Talk Manus**](https://www.youtube.com/watch?v=6_BcCthVvb8&start=2525)
|
| 40 |
+
- [**LangGraph Overview**](https://docs.langchain.com/oss/python/langgraph/overview)
|
| 41 |
+
- https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents
|
| 42 |
+
- https://medium.com/fundamentals-of-artificial-intelligence/mitigate-context-poisoning-in-ai-agents-using-context-engineering-96cf40dbb38d
|
| 43 |
+
- https://blog.langchain.com/context-engineering-for-agents/
|
| 44 |
+
- **langgraph implementations**
|
| 45 |
+
- [video]((https://www.youtube.com/watch?v=nyKvyRrpbyY))
|
| 46 |
+
- [good notebooks](https://github.com/langchain-ai/how_to_fix_your_context/blob/main/notebooks/utils.py)
|
| 47 |
+
- [Langgraph summary of what frontier labs and firms apply](https://www.youtube.com/watch?v=XFCkrYHHfpQ)
|
| 48 |
+
|
| 49 |
+
These resources guide our approach to **memory management, planning transparency, and tool orchestration** in autonomous agents.
|
| 50 |
+
|
| 51 |
+
## 🧾 ***`HR Candidate Screening Multi-Agent System`***
|
| 52 |
+
An autonomous HR assistant that streamlines early recruitment through five steps:
|
| 53 |
+
1. **CV Upload (Application)** — candidate applications uploaded and parsed
|
| 54 |
+
2. **CV Screening** — rank and shortlist candidates using LLM reasoning
|
| 55 |
+
3. **Voice Screening** — invite and coordinate interviews using a voice agent.
|
| 56 |
+
4. **Person-to-Person Screening** — schedule HR interviews via Google Calendar integration
|
| 57 |
+
5. **Decision** — generate a concise summary and notify HR
|
| 58 |
+
|
| 59 |
+
> **`NOTE`**
|
| 60 |
+
> - Final decision of whether candidate will be hired is made by human.
|
| 61 |
+
> - Just automate the boring, tedious stuff while keeping human final decision in the loop.
|
| 62 |
+
|
| 63 |
+
**Architecture:**
|
| 64 |
+
1. **Main Planner Agent**: orchestrates the workflow
|
| 65 |
+
2. **Subagents**:
|
| 66 |
+
- CV Screening Agent
|
| 67 |
+
- Voice Screening Agent
|
| 68 |
+
- Meeting Scheduler Agent
|
| 69 |
+
3. **Tools (via MCP)** connect to Gmail, Calendar, and Voice APIs.
|
| 70 |
+
4. **Database** stores both candidate info and persistent agent memory.
|
| 71 |
+
5. **Gradio UI** visualizes workflow, reasoning, and results.
|
| 72 |
+
```mermaid
|
| 73 |
+
flowchart TD
|
| 74 |
+
subgraph MainAgent["🧠 Main Planner Agent"]
|
| 75 |
+
A1["Plans • Reasons • Executes"]
|
| 76 |
+
end
|
| 77 |
+
|
| 78 |
+
subgraph Subagents["🤖 Subagents"]
|
| 79 |
+
S1["📄 CV Screening"]
|
| 80 |
+
S2["🎙️ Voice Screening"]
|
| 81 |
+
S3["📅 Scheduling"]
|
| 82 |
+
S4["🧾 Decision Summary"]
|
| 83 |
+
end
|
| 84 |
+
|
| 85 |
+
subgraph Tools["⚙️ MCP & External Tools"]
|
| 86 |
+
T1["📧 Gmail"]
|
| 87 |
+
T2["🗓️ Google Calendar"]
|
| 88 |
+
T3["🗣️ Voice API"]
|
| 89 |
+
end
|
| 90 |
+
|
| 91 |
+
subgraph Data["🗄️ Database"]
|
| 92 |
+
D1["Candidate Data"]
|
| 93 |
+
D2["Context Memory (Cognitive Offloading)"]
|
| 94 |
+
end
|
| 95 |
+
|
| 96 |
+
subgraph UI["💬 Gradio Dashboard"]
|
| 97 |
+
U1["HR View & Interaction"]
|
| 98 |
+
end
|
| 99 |
+
|
| 100 |
+
%% Connections
|
| 101 |
+
MainAgent --> Subagents
|
| 102 |
+
Subagents --> Tools
|
| 103 |
+
Subagents --> Data
|
| 104 |
+
MainAgent --> Data
|
| 105 |
+
MainAgent --> UI
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
**GCP Setup for Judges:**
|
| 109 |
+
A single demo Gmail/Calendar account (`scionhire.demo@gmail.com`) is pre-authorized via OAuth, with stored credentials in `.env`.
|
| 110 |
+
Judges can run or view the live demo without any credential setup, experiencing real Gmail + Calendar automation safely.
|
| 111 |
+
|
| 112 |
+
We use **hierarchical planning**:
|
| 113 |
+
- **Main Agent:** decides next step in the workflow (plan, adapt, replan)
|
| 114 |
+
- **Subagents:** specialized executors (screening, scheduling, summarization)
|
| 115 |
+
- **Memory State:** tracks plan progress and tool results
|
| 116 |
+
- **Dashboard Visualization:** shows active plan steps and reasoning traces for transparency
|
| 117 |
+
|
| 118 |
+
🧠 Why This Is an Agent (Not Just a Workflow)
|
| 119 |
+
|
| 120 |
+
| Criterion | Workflow | Our System |
|
| 121 |
+
|------------|-----------|-------------|
|
| 122 |
+
| **Autonomy** | Executes fixed sequence of steps | Main agent decides next actions without manual triggers |
|
| 123 |
+
| **Planning** | Predefined order (A → B → C) | Main agent generates and adapts a plan (e.g., skip, retry, re-order) |
|
| 124 |
+
| **Reasoning** | No decision logic | Uses LLM reasoning to evaluate outputs and choose next subagent |
|
| 125 |
+
| **Context Awareness** | Stateless | Maintains shared memory of candidates, progress, and outcomes |
|
| 126 |
+
| **Adaptation** | Fails or stops on error | Re-plans (e.g., if calendar slots full or candidate unresponsive) |
|
| 127 |
+
|
| 128 |
+
✅ **Therefore:** it qualifies as an *agentic system* because it **plans, reasons, and executes** autonomously rather than following a static workflow.
|
| 129 |
+
|
| 130 |
+
## ***`Project Structure`***
|
| 131 |
+
```
|
| 132 |
+
agentic-hr/
|
| 133 |
+
│
|
| 134 |
+
├── 📁 src/
|
| 135 |
+
│ │
|
| 136 |
+
│ ├── 📁 core/
|
| 137 |
+
│ │ │ ├── base_agent.py # Abstract BaseAgent (LangGraph-compatible)
|
| 138 |
+
│ │ │ ├── supervisor.py # Supervisor agent (LangGraph graph assembly)
|
| 139 |
+
│ │ │ ├── state.py # Shared AgentState + context window
|
| 140 |
+
│ │ │ ├── planner.py # High-level planning logic
|
| 141 |
+
│ │ │ └── executor.py # Graph executor / runner
|
| 142 |
+
│ │
|
| 143 |
+
│ ├── 📁 agents/
|
| 144 |
+
│ │ │
|
| 145 |
+
│ │ ├── 📁 cv_screening/
|
| 146 |
+
│ │ │ │ ├── agent.py # CVScreeningAgent implementation
|
| 147 |
+
│ │ │ │ ├── 📁 tools/
|
| 148 |
+
│ │ │ │ │ ├── doc_parser.py
|
| 149 |
+
│ │ │ │ │ ├── normalize_skills.py
|
| 150 |
+
│ │ │ │ │ ├── rank_candidates.py
|
| 151 |
+
│ │ │ │ │ └── match_to_jd.py
|
| 152 |
+
│ │ │ │ └── 📁 schemas/
|
| 153 |
+
│ │ │ │ ├── cv_schema.py # Parsed CV Pydantic schema
|
| 154 |
+
│ │ │ │ └── jd_schema.py # Job description schema
|
| 155 |
+
│ │ │
|
| 156 |
+
│ │ ├── 📁 voice_screening/
|
| 157 |
+
│ │ │ │ ├── agent.py # VoiceScreeningAgent
|
| 158 |
+
│ │ │ │ ├── 📁 tools/
|
| 159 |
+
│ │ │ │ │ ├── twilio_client.py
|
| 160 |
+
│ │ │ │ │ ├── whisper_transcribe.py
|
| 161 |
+
│ │ │ │ │ └── tts_service.py
|
| 162 |
+
│ │ │ │ └── 📁 schemas/
|
| 163 |
+
│ │ │ │ ├── call_result.py
|
| 164 |
+
│ │ │ │ └── transcript.py
|
| 165 |
+
│ │ │
|
| 166 |
+
│ │ ├── 📁 scheduler/
|
| 167 |
+
│ │ │ │ ├── agent.py # SchedulerAgent
|
| 168 |
+
│ │ │ │ ├── 📁 tools/
|
| 169 |
+
│ │ │ │ │ ├── calendar_tool.py
|
| 170 |
+
│ │ │ │ │ ├── gmail_tool.py
|
| 171 |
+
│ │ │ │ │ └── slot_optimizer.py
|
| 172 |
+
│ │ │ │ └── 📁 schemas/
|
| 173 |
+
│ │ │ │ └── meeting_schema.py
|
| 174 |
+
│ │ │
|
| 175 |
+
│ │ └── 📁 decision/
|
| 176 |
+
│ │ ├── agent.py # DecisionAgent (final summarizer/Reporter)
|
| 177 |
+
│ │ └── 📁 schemas/
|
| 178 |
+
│ │ └── decision_report.py
|
| 179 |
+
│ │
|
| 180 |
+
│ ├── 📁 mcp_server/
|
| 181 |
+
│ │ ├── main.py
|
| 182 |
+
│ │ ├── 📁 endpoints/
|
| 183 |
+
│ │ ├── auth.py
|
| 184 |
+
│ │ └── schemas.py
|
| 185 |
+
│ │
|
| 186 |
+
│ ├── 📁 gradio/
|
| 187 |
+
│ │ ├── app.py # Main Gradio app (Hugging Face Space entry)
|
| 188 |
+
│ │ ├── dashboard.py # Live agent graph & logs view
|
| 189 |
+
│ │ ├── candidate_portal.py # Candidate upload / screening status
|
| 190 |
+
│ │ ├── hr_portal.py # HR review + interview approval
|
| 191 |
+
│ │ ├── components.py # Shared Gradio components
|
| 192 |
+
│ │ └── 📁 assets/ # Logos, CSS, etc.
|
| 193 |
+
│ │
|
| 194 |
+
│ ├── 📁 cv_ui/
|
| 195 |
+
│ │ ├── app.py
|
| 196 |
+
│ │
|
| 197 |
+
��� ├── 📁 voice_screening_ui/
|
| 198 |
+
│ │ ├── app.py
|
| 199 |
+
│ │
|
| 200 |
+
│ │
|
| 201 |
+
│ ├── 📁 prompts/
|
| 202 |
+
│ │ ├── prompt_manager.py # Centralized prompt versioning
|
| 203 |
+
│ │ ├── cv_prompts.py
|
| 204 |
+
│ │ ├── voice_prompts.py
|
| 205 |
+
│ │ └── scheduler_prompts.py
|
| 206 |
+
│ │
|
| 207 |
+
│ ├── 📁 database/
|
| 208 |
+
│ │ ├── models.py # SQLAlchemy models
|
| 209 |
+
│ │ ├── db_client.py # Connection & CRUD
|
| 210 |
+
│ │ └── context_sync.py # Cognitive offloading (context ⇄ DB)
|
| 211 |
+
│ │
|
| 212 |
+
│ ├── main.py # CLI runner / local orchestrator entry
|
| 213 |
+
│ └── config.py # Environment configuration
|
| 214 |
+
│
|
| 215 |
+
├── 📁 tests/
|
| 216 |
+
│ │ ├── test_cv_agent.py
|
| 217 |
+
│ │ ├── test_voice_agent.py
|
| 218 |
+
│ │ ├── test_scheduler_agent.py
|
| 219 |
+
│ │ ├── test_mcp_server.py
|
| 220 |
+
│ │ └── test_integration.py
|
| 221 |
+
│
|
| 222 |
+
├── .env.example
|
| 223 |
+
├── requirements.txt
|
| 224 |
+
├── Dockerfile
|
| 225 |
+
├── app.py # Shortcut to src/ui/app.py
|
| 226 |
+
├── README.md
|
| 227 |
+
└── LICENSE
|
| 228 |
+
```
|
| 229 |
+
|
| 230 |
+
## ***`Multi Agent System Architecture`***
|
| 231 |
+
Below you will find an overview of the subagent components that mnake upo the entire system. More detailed information and brainstorming is decicated to the `docs/agents/..` directory.
|
| 232 |
+
|
| 233 |
+
### 1) ***`Orchestrator`***
|
| 234 |
+
#### Overview
|
| 235 |
+
|
| 236 |
+
The orchestrator agent is reponsible for **supervising** and **triggering** the ***tasks of the subagents***.
|
| 237 |
+
|
| 238 |
+
> For more planning and info, go to `docs/agents/agent_orchestrator.md`
|
| 239 |
+
|
| 240 |
+
### 2) ***`CV Screener`***
|
| 241 |
+
#### Overview
|
| 242 |
+
The cv screening agent deals with scanning the applicant's CV's, and deciding who are fruitful versus unpromising candidates as a first filtering step.
|
| 243 |
+
|
| 244 |
+
> For more planning and info, go to `docs/agents/cv_screening.md`
|
| 245 |
+
|
| 246 |
+
### 3) 🎙️ ***`Voice Screening Agent`***
|
| 247 |
+
|
| 248 |
+
#### Overview
|
| 249 |
+
The **Voice Screening Agent** conducts automated phone interviews and integrates with the **LangGraph HR Orchestrator**.
|
| 250 |
+
It uses **Twilio** for phone calls, **Whisper/ASR** for speech-to-text, **ElevenLabs** for natural voice output, and **LangGraph** for dialogue logic.
|
| 251 |
+
|
| 252 |
+
> For more planning and info, go to `docs/agents/voice_screening.md`
|
| 253 |
+
|
| 254 |
+
### 4) ***`Google MCP Agents`***
|
| 255 |
+
#### Overview
|
| 256 |
+
The google mcp agents will be resposnible to:
|
| 257 |
+
a) writing emails
|
| 258 |
+
b) scheduling and menaging google calendar events
|
| 259 |
+
|
| 260 |
+
It adviseable to break this up into two subagents, to get rid of `context poisoning`.
|
| 261 |
+
|
| 262 |
+
> For more planning and info, go to `docs/agents/google_mcp_agent.md`
|
| 263 |
+
|
| 264 |
+
### 4) ***`LLM as a Judge`***
|
| 265 |
+
#### Overview
|
| 266 |
+
LLM-as-a-judge will be leveraged to judge call screening results.
|
| 267 |
+
|
| 268 |
+
> For more planning and info, go to `docs/agents/judging_agent.md`
|
| 269 |
+
|
| 270 |
+
## 🗄️ ***`Data Layer`***
|
| 271 |
+
|
| 272 |
+
The system uses a unified **SQLAlchemy-based database** for both **candidate data management** and **context engineering**.
|
| 273 |
+
|
| 274 |
+
### 📦 Purpose
|
| 275 |
+
| Data Type | Description |
|
| 276 |
+
|------------|--------------|
|
| 277 |
+
| 🧾 **Candidates** | Stores CVs, parsed data, and screening results |
|
| 278 |
+
| 🎙️ **Voice Results** | Saves transcripts, evaluations, and tone analysis |
|
| 279 |
+
| 🗓️ **Scheduling** | Tracks HR availability and confirmed interviews |
|
| 280 |
+
| 🧠 **Agent Context Memory** | Enables **cognitive offloading** — storing reasoning traces and summaries so the active context stays uncluttered and information can be recalled when needed |
|
| 281 |
+
| 📚 **Logs / Tool History** | Archives tool interactions and results for transparency and reuse |
|
| 282 |
+
|
| 283 |
+
We use [**SQLAlchemy**](https://www.sqlalchemy.org) as the ORM layer to manage both structured candidate data and **persistent agent memory**, allowing the system to offload, summarize, and retrieve context efficiently across sessions.
|
| 284 |
+
|
| 285 |
+
## 🗃️ ***`Prompt Archive`***
|
| 286 |
+
|
| 287 |
+
To ensure consistent behavior and easy experimentation across subagents, the system includes a **centralized prompt management layer**.
|
| 288 |
+
|
| 289 |
+
### 📦 Purpose
|
| 290 |
+
| Component | Description |
|
| 291 |
+
|------------|--------------|
|
| 292 |
+
| 🧠 **Prompt Templates** | Stores standardized prompts for each subagent (CV screening, voice screening, scheduling) |
|
| 293 |
+
| 🔄 **Prompt Versioning** | Allows tracking and updating of prompt iterations without changing agent code |
|
| 294 |
+
| 🧩 **Dynamic Injection** | Enables context-dependent prompt construction using retrieved memory or database summaries |
|
| 295 |
+
| 📚 **Archive** | Keeps older prompt variants for reproducibility and ablation testing |
|
| 296 |
+
|
| 297 |
+
## 📺 ***`Gradio Interface`***
|
| 298 |
+
|
| 299 |
+
We use **Gradio** to demonstrate our agent's reasoning, planning, and tool use interactively — fully aligned with the **Agents & MCP Hackathon** focus on **context engineering** and **user value**.
|
| 300 |
+
|
| 301 |
+
### 🧩 Key Features
|
| 302 |
+
| Section | Purpose |
|
| 303 |
+
|----------|----------|
|
| 304 |
+
| 🧍 **Candidate Portal** | Upload CVs, submit applications, and view screening results |
|
| 305 |
+
| 🧑💼 **HR Portal** | Review shortlisted candidates, trigger voice screenings, and schedule interviews |
|
| 306 |
+
| 🧠 **Agent Dashboard** | Visualizes the current plan, tool calls, and reasoning traces in real time |
|
| 307 |
+
| ⚙️ **Tool Integration** | Shows live MCP actions (Gmail send, Calendar scheduling) with status updates |
|
| 308 |
+
| 📊 **Context View** | Displays agent memory, current workflow stage, and adaptive plan updates |
|
| 309 |
+
|
| 310 |
+
#### Context Engineering Visualization?
|
| 311 |
+
This is what judges really care about — it must show that the system is agentic (reasoning, memory, planning).
|
| 312 |
+
🧠 Agent Plan Viewer
|
| 313 |
+
gr.JSON() or custom visual showing the current plan state, e.g.:
|
| 314 |
+
```json
|
| 315 |
+
{
|
| 316 |
+
"plan": [
|
| 317 |
+
"1. Screen CVs ✅",
|
| 318 |
+
"2. Invite for voice screening 🔄",
|
| 319 |
+
"3. Schedule HR interview ⬜",
|
| 320 |
+
"4. Await HR decision ⬜"
|
| 321 |
+
]
|
| 322 |
+
}
|
| 323 |
+
```
|
| 324 |
+
🗺️ Live Plan Progress
|
| 325 |
+
- Use a progress bar or color-coded status list of steps.
|
| 326 |
+
- Judges must see autonomous transitions (from one step to another).
|
| 327 |
+
|
| 328 |
+
💬 Reasoning Log / Memory
|
| 329 |
+
- Stream or text box showing LLM thought traces or context summary:
|
| 330 |
+
- “Detected strong match for Data Scientist role.”
|
| 331 |
+
- “Candidate completed voice interview; confidence: 8.4/10.”
|
| 332 |
+
- “Next step: scheduling HR interview.”
|
| 333 |
+
|
| 334 |
+
⚙️ Tool Call Trace
|
| 335 |
+
- Small table showing:
|
| 336 |
+
|
| 337 |
+
| Time | Tool | Action | Result |
|
| 338 |
+
| ----- | -------- | ---------------- | --------- |
|
| 339 |
+
| 12:05 | Gmail | `send_invite()` | Sent |
|
| 340 |
+
| 12:06 | Calendar | `create_event()` | Confirmed |
|
| 341 |
+
|
| 342 |
+
## 🔗 ***`MCP Integration (Best Practice Setup)`***
|
| 343 |
+
|
| 344 |
+
To align fully with the **Agents & MCP Hackathon** standards, our system will use or extend a **standardized MCP server** for integrations such as **Gmail** and **Google Calendar** — and potentially **Scion Voice** in later stages.
|
| 345 |
+
|
| 346 |
+
**`Inspired by`** [Huggingface MCP Course](https://huggingface.co/learn/mcp-course/en/unit2/introduction): shows how to build an MCP app.
|
| 347 |
+
|
| 348 |
+
### 🧩 Why MCP?
|
| 349 |
+
| Benefit | Description |
|
| 350 |
+
|----------|--------------|
|
| 351 |
+
| ✅ **Standardized** | Exposes Gmail & Calendar as reusable MCP tools with a consistent schema |
|
| 352 |
+
| 🔐 **Secure** | OAuth handled once server-side — no tokens or secrets stored in the agent |
|
| 353 |
+
| 🧱 **Modular** | Clean separation between the agent's reasoning logic and the integration layer |
|
| 354 |
+
| 🔄 **Reusable** | Same MCP server can serve multiple projects or agents |
|
| 355 |
+
| 🚀 **Hackathon-Ready** | Directly fulfills the “use MCP tools or external APIs” requirement |
|
| 356 |
+
|
| 357 |
+
---
|
| 358 |
+
|
| 359 |
+
### ⚙️ Why Use MCP Instead of Just Defining Tools
|
| 360 |
+
| Approach | Limitation / Risk | MCP Advantage |
|
| 361 |
+
|-----------|-------------------|----------------|
|
| 362 |
+
| **Custom-defined tools** (e.g., direct Gmail API calls in code) | Each project must re-implement auth, rate limits, and API logic | MCP provides a *shared, pre-authorized* interface any agent can use |
|
| 363 |
+
| **Embedded credentials** in `.env` | Security risk, harder for judges to test | Credentials handled server-side — no secrets in the repo |
|
| 364 |
+
| **Tight coupling** between agent and tool | Hard to swap or extend integrations | MCP creates a plug-and-play API boundary between reasoning and execution |
|
| 365 |
+
| **Limited reuse** | Tools only exist in one codebase | MCP servers can expose many tools to multiple agents dynamically |
|
| 366 |
+
|
| 367 |
+
MCP turns these one-off integrations into **standardized, composable building blocks** that work across agents, organizations, or platforms — the same philosophy used by **Anthropic**, **LangChain**, and **Hugging Face** in 2025 agent ecosystems.
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
We will build or extend the open-source [**mcp-gsuite**](https://github.com/MarkusPfundstein/mcp-gsuite) server and host it securely on **Google Cloud Run**.
|
| 371 |
+
This server manages authentication, token refresh, and rate limiting — while exposing standardized MCP actions like:
|
| 372 |
+
```json
|
| 373 |
+
{
|
| 374 |
+
"action": "gmail.send",
|
| 375 |
+
"parameters": { "to": "candidate@example.com", "subject": "Interview Invite", "body": "..." }
|
| 376 |
+
}
|
| 377 |
+
```
|
| 378 |
+
|
| 379 |
+
and
|
| 380 |
+
|
| 381 |
+
```json
|
| 382 |
+
{
|
| 383 |
+
"action": "calendar.create_event",
|
| 384 |
+
"parameters": { "summary": "HR Interview", "start": "...", "end": "..." }
|
| 385 |
+
}
|
| 386 |
+
```
|
| 387 |
+
This architecture lets our HR agent (and future projects) perform real email and scheduling actions via secure MCP endpoints — giving judges a safe, live demo of true agentic behavior with no local credential setup required.
|
| 388 |
+
|
| 389 |
+
## 🧠 ***`Agent Supervisor — Why Parlant + LangGraph`***
|
| 390 |
+
|
| 391 |
+
LangGraph provides a powerful orchestration backbone for planning, reasoning, and executing multi-agent workflows.
|
| 392 |
+
However, its common **supervisor pattern** has a key limitation: the supervisor routes each user query to **only one sub-agent** at a time.
|
| 393 |
+
|
| 394 |
+
### ⚠️ Example Problem
|
| 395 |
+
> “I uploaded my CV yesterday. Can I also reschedule my interview — and how long is the voice call?”
|
| 396 |
+
|
| 397 |
+
A standard LangGraph supervisor would forward this entire message to, say, the **CV Screening Agent**,
|
| 398 |
+
missing the **scheduling** and **voice screening** parts — causing incomplete or fragmented responses.
|
| 399 |
+
|
| 400 |
+
### 💡 Parlant as the Fix
|
| 401 |
+
**[Parlant](https://github.com/emcie-co/parlant)** solves this by replacing single-route logic with **dynamic guideline activation**.
|
| 402 |
+
Instead of rigid routing, it loads multiple relevant *guidelines* into context simultaneously, allowing coherent handling of mixed intents.
|
| 403 |
+
|
| 404 |
+
```python
|
| 405 |
+
agent.create_guideline(
|
| 406 |
+
condition="User asks about rescheduling",
|
| 407 |
+
action="Call SchedulerAgent via LangGraph tool"
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
agent.create_guideline(
|
| 411 |
+
condition="User asks about voice screening duration",
|
| 412 |
+
action="Query VoiceScreeningAgent"
|
| 413 |
+
)
|
| 414 |
+
```
|
| 415 |
+
|
| 416 |
+
If a user blends both topics, ***both guidelines trigger***, producing a unified, context-aware response.
|
| 417 |
+
|
| 418 |
+
### ⚙️ Why Combine Them
|
| 419 |
+
| Layer | Framework | Role |
|
| 420 |
+
| ----------------------------- | ------------- | ----------------------------------------------------------------------- |
|
| 421 |
+
| 🧠 **Workflow Orchestration** | **LangGraph** | Executes structured agent workflows (CV → Voice → Schedule → Decision). |
|
| 422 |
+
| 💬 **Conversational Layer** | **Parlant** | Dynamically manages mixed intents using guideline-based reasoning. |
|
| 423 |
+
| 🔧 **Integration Layer** | **MCP Tools** | Provides standardized access to Gmail, Calendar, and Voice APIs. |
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
Together, ***Parlant + LangGraph*** merge structured planning with conversational adaptability —
|
| 427 |
+
enabling our HR agent to reason, plan, and respond naturally to complex, multi-topic interactions.
|
| 428 |
+
|
| 429 |
+
## ✨ ***`Agentic Enhancements [BONUS]`***
|
| 430 |
+
|
| 431 |
+
To make the system more **autonomous, interpretable, and resilient**, we integrated a few lightweight yet powerful improvements:
|
| 432 |
+
|
| 433 |
+
- 🧠 **Self-Reflection** – before executing a step, the agent briefly states *why* it's taking that action, improving reasoning transparency.
|
| 434 |
+
- 🔄 **Adaptive Re-Planning** – if a subagent or tool call fails (e.g., no calendar slot, missing response, or API timeout), the main planner automatically updates its plan — skipping, retrying, or re-ordering steps instead of stopping.
|
| 435 |
+
- 🧮 **LLM Self-Evaluation** – after each stage (CV, voice, scheduling), a lightweight judge model rates the result and adds feedback for the next step.
|
| 436 |
+
- 🗂️ **Context Summary** – the dashboard displays a live summary of all candidates, their current stage, and key outcomes.
|
| 437 |
+
- 🤝 **Human-in-the-Loop Checkpoint** – HR receives a short confirmation prompt before final scheduling to ensure responsible autonomy.
|
| 438 |
+
|
| 439 |
+
These enhancements demonstrate **true agentic behavior** — autonomous planning, adaptive execution, and transparent reasoning — in a simple, explainable way.
|
| 440 |
+
|
| 441 |
+
## 👥 ***`Team`***
|
| 442 |
+
| Member |
|
| 443 |
+
| -------- |
|
| 444 |
+
| [Sebastian Wefers](https://github.com/Ocean-code-1995) |
|
| 445 |
+
| [Owen Kaplinsky](https://github.com/owenkaplinsky) |
|
| 446 |
+
| [SrikarMK](https://github.com/Srikarmk) |
|
| 447 |
+
| [Dmitri Moscoglo](https://github.com/DimiM99) |
|
| 448 |
+
|
| 449 |
+
# ***`License`***
|
| 450 |
+
|
| 451 |
+
This project includes and builds upon [gmail-mcp](https://github.com/theposch/gmail-mcp),
|
| 452 |
+
which is licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
|
| 453 |
+
|
| 454 |
+
This repository extends gmail-mcp for experimental integration and automation with Claude Desktop.
|
| 455 |
+
All modifications are distributed under the same GPLv3 license.
|
| 456 |
+
|
| 457 |
+
> **Note:** The original gmail-mcp code has not been modified at this stage.
|
langgraph.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"dependencies": ["./src"],
|
| 3 |
+
"graphs": {
|
| 4 |
+
"supervisor": "src.agents.supervisor.supervisor_v2:supervisor_agent"
|
| 5 |
+
}
|
| 6 |
+
}
|
| 7 |
+
|
package-lock.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "recruitment-agent3",
|
| 3 |
+
"lockfileVersion": 3,
|
| 4 |
+
"requires": true,
|
| 5 |
+
"packages": {}
|
| 6 |
+
}
|
requirements/agent.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
langchain
|
| 2 |
+
langchain-openai
|
| 3 |
+
langgraph
|
requirements/all.txt
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Consolidated requirements with all dependencies
|
| 2 |
+
# Deduplicated from all individual requirement files
|
| 3 |
+
|
| 4 |
+
# Base dependencies
|
| 5 |
+
python-dotenv
|
| 6 |
+
pydantic>=2.0
|
| 7 |
+
pydantic-settings
|
| 8 |
+
promptlayer
|
| 9 |
+
|
| 10 |
+
# LangChain & LangGraph
|
| 11 |
+
langchain
|
| 12 |
+
langchain-openai
|
| 13 |
+
langchain-community
|
| 14 |
+
langchain-mcp-adapters
|
| 15 |
+
langgraph
|
| 16 |
+
langgraph-cli
|
| 17 |
+
|
| 18 |
+
# API Framework
|
| 19 |
+
fastapi
|
| 20 |
+
uvicorn[standard]
|
| 21 |
+
|
| 22 |
+
# Web & HTTP
|
| 23 |
+
websockets>=12.0
|
| 24 |
+
requests
|
| 25 |
+
aiohttp
|
| 26 |
+
httpx>=0.28.1
|
| 27 |
+
|
| 28 |
+
# Database
|
| 29 |
+
sqlalchemy
|
| 30 |
+
psycopg2-binary
|
| 31 |
+
|
| 32 |
+
# Google APIs
|
| 33 |
+
google-auth
|
| 34 |
+
google-auth-oauthlib
|
| 35 |
+
google-auth-httplib2
|
| 36 |
+
google-api-python-client
|
| 37 |
+
|
| 38 |
+
# MCP Server
|
| 39 |
+
mcp
|
| 40 |
+
|
| 41 |
+
# OpenAI
|
| 42 |
+
openai
|
| 43 |
+
|
| 44 |
+
# UI & Visualization
|
| 45 |
+
streamlit
|
| 46 |
+
rich
|
| 47 |
+
|
| 48 |
+
# PDF & Image Processing
|
| 49 |
+
pypdfium2
|
| 50 |
+
pillow
|
| 51 |
+
ftfy
|
| 52 |
+
|
| 53 |
+
# Search & Tools
|
| 54 |
+
duckduckgo-search
|
| 55 |
+
langchain-tavily
|
| 56 |
+
|
| 57 |
+
# Email validation
|
| 58 |
+
email-validator
|
| 59 |
+
pydantic[email]
|
| 60 |
+
|
| 61 |
+
# Date utilities
|
| 62 |
+
python-dateutil
|
| 63 |
+
|
| 64 |
+
gradio==6.0.1
|
requirements/api.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# API Layer dependencies
|
| 2 |
+
-r base.txt
|
| 3 |
+
|
| 4 |
+
fastapi
|
| 5 |
+
uvicorn[standard]
|
| 6 |
+
pydantic>=2.0
|
| 7 |
+
|
requirements/base.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
python-dotenv
|
| 2 |
+
pydantic
|
| 3 |
+
pydantic-settings
|
| 4 |
+
promptlayer
|
requirements/cv_ui.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CV Upload UI requirements
|
| 2 |
+
# Includes base requirements
|
| 3 |
+
-r base.txt
|
| 4 |
+
-r db.txt
|
| 5 |
+
|
| 6 |
+
openai
|
| 7 |
+
|
| 8 |
+
# Streamlit for UI
|
| 9 |
+
streamlit
|
| 10 |
+
|
| 11 |
+
# PDF processing
|
| 12 |
+
pypdfium2
|
| 13 |
+
pillow
|
| 14 |
+
ftfy
|
| 15 |
+
|
requirements/db.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# data base requirements
|
| 2 |
+
-r base.txt
|
| 3 |
+
sqlalchemy
|
| 4 |
+
psycopg2-binary
|
requirements/mcp_calendar.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Google Calendar MCP Server requirements
|
| 2 |
+
# Includes base requirements
|
| 3 |
+
-r base.txt
|
| 4 |
+
|
| 5 |
+
# MCP server framework
|
| 6 |
+
mcp
|
| 7 |
+
|
| 8 |
+
# Google Calendar API
|
| 9 |
+
google-auth
|
| 10 |
+
google-auth-oauthlib
|
| 11 |
+
google-api-python-client
|
| 12 |
+
|
| 13 |
+
# Date utilities
|
| 14 |
+
python-dateutil
|
| 15 |
+
|
requirements/mcp_gmail.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Gmail MCP Server requirements
|
| 2 |
+
# Includes base requirements
|
| 3 |
+
-r base.txt
|
| 4 |
+
|
| 5 |
+
# MCP server framework
|
| 6 |
+
mcp
|
| 7 |
+
|
| 8 |
+
# Gmail API
|
| 9 |
+
google-auth
|
| 10 |
+
google-auth-oauthlib
|
| 11 |
+
google-api-python-client
|
| 12 |
+
google-auth-httplib2
|
| 13 |
+
|
| 14 |
+
# HTTP client
|
| 15 |
+
httpx>=0.28.1
|
| 16 |
+
|
requirements/requirements.txt
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
google-auth
|
| 2 |
+
langgraph
|
| 3 |
+
langchain
|
| 4 |
+
langchain-openai
|
| 5 |
+
langchain-mcp-adapters
|
| 6 |
+
duckduckgo-search
|
| 7 |
+
langchain-tavily
|
| 8 |
+
langchain-community
|
| 9 |
+
langgraph-cli
|
| 10 |
+
promptlayer
|
| 11 |
+
|
| 12 |
+
mcp
|
| 13 |
+
pypdfium2
|
| 14 |
+
pillow
|
| 15 |
+
ftfy
|
| 16 |
+
sqlalchemy
|
| 17 |
+
psycopg2-binary
|
| 18 |
+
pydantic
|
| 19 |
+
pydantic-settings
|
| 20 |
+
python-dotenv
|
| 21 |
+
openai
|
| 22 |
+
pypdfium2
|
| 23 |
+
Pillow
|
| 24 |
+
ftfy
|
requirements/supervisor.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Supervisor Agent requirements
|
| 2 |
+
# Includes base requirements
|
| 3 |
+
-r base.txt
|
| 4 |
+
|
| 5 |
+
# LangGraph for agent orchestration
|
| 6 |
+
langgraph
|
| 7 |
+
langchain-community
|
| 8 |
+
langchain-mcp-adapters
|
| 9 |
+
streamlit
|
| 10 |
+
rich
|
| 11 |
+
requests
|
| 12 |
+
|
requirements/voice_proxy.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-r base.txt
|
| 2 |
+
-r db.txt
|
| 3 |
+
fastapi
|
| 4 |
+
uvicorn[standard]
|
| 5 |
+
websockets>=12.0
|
| 6 |
+
requests
|
| 7 |
+
asyncio
|
| 8 |
+
aiohttp
|
| 9 |
+
email-validator
|
| 10 |
+
pydantic[email]
|
requirements/voice_screening_ui.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-r base.txt
|
| 2 |
+
streamlit
|
| 3 |
+
uvicorn[standard]
|
| 4 |
+
requests
|
| 5 |
+
|
| 6 |
+
|
scripts/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CLI scripts package."""
|
| 2 |
+
|
scripts/db/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Database CLI utilities."""
|
| 2 |
+
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
# Add project root to sys.path for all db scripts
|
| 7 |
+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))
|
| 8 |
+
if project_root not in sys.path:
|
| 9 |
+
sys.path.insert(0, project_root)
|
| 10 |
+
|
scripts/db/debug_all.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Run all database debug checks.
|
| 3 |
+
|
| 4 |
+
Run as follows:
|
| 5 |
+
>>> POSTGRES_HOST=localhost POSTGRES_PORT=5433 POSTGRES_PASSWORD=password123 python -m scripts.db.debug_all
|
| 6 |
+
|
| 7 |
+
This runs:
|
| 8 |
+
1. Connection test
|
| 9 |
+
2. Session query test
|
| 10 |
+
3. List existing candidates
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import sys
|
| 14 |
+
import os
|
| 15 |
+
|
| 16 |
+
# Add project root to sys.path
|
| 17 |
+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))
|
| 18 |
+
if project_root not in sys.path:
|
| 19 |
+
sys.path.insert(0, project_root)
|
| 20 |
+
|
| 21 |
+
from scripts.db.test_connection import test_connection
|
| 22 |
+
from scripts.db.test_session import test_session_query
|
| 23 |
+
from scripts.db.list_candidates import list_candidates
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def run_all_checks() -> None:
|
| 27 |
+
"""Run all database diagnostic checks."""
|
| 28 |
+
print("=" * 50)
|
| 29 |
+
print("🔍 DATABASE DIAGNOSTICS")
|
| 30 |
+
print("=" * 50)
|
| 31 |
+
|
| 32 |
+
# 1. Test connection
|
| 33 |
+
conn_ok = test_connection()
|
| 34 |
+
|
| 35 |
+
if not conn_ok:
|
| 36 |
+
print("\n⛔ Stopping - connection failed")
|
| 37 |
+
return
|
| 38 |
+
|
| 39 |
+
print()
|
| 40 |
+
|
| 41 |
+
# 2. Test session
|
| 42 |
+
session_ok = test_session_query()
|
| 43 |
+
|
| 44 |
+
if not session_ok:
|
| 45 |
+
print("\n⛔ Stopping - session failed")
|
| 46 |
+
return
|
| 47 |
+
|
| 48 |
+
print()
|
| 49 |
+
|
| 50 |
+
# 3. List candidates
|
| 51 |
+
list_candidates()
|
| 52 |
+
|
| 53 |
+
print()
|
| 54 |
+
print("=" * 50)
|
| 55 |
+
print("✅ All checks completed")
|
| 56 |
+
print("=" * 50)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
if __name__ == "__main__":
|
| 60 |
+
run_all_checks()
|
scripts/db/list_candidates.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
List candidates in the database.
|
| 3 |
+
|
| 4 |
+
Run standalone:
|
| 5 |
+
>>> POSTGRES_HOST=localhost POSTGRES_PORT=5433 python scripts/db/list_candidates.py
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from sqlalchemy.exc import ProgrammingError
|
| 9 |
+
|
| 10 |
+
# Ensure project root is in path
|
| 11 |
+
import scripts.db # noqa: F401
|
| 12 |
+
|
| 13 |
+
from src.database.candidates.client import SessionLocal
|
| 14 |
+
from src.database.candidates.models import Candidate
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def list_candidates(limit: int = 10) -> bool:
|
| 18 |
+
"""
|
| 19 |
+
Check and list existing candidates in the database.
|
| 20 |
+
|
| 21 |
+
Args:
|
| 22 |
+
limit: Maximum number of candidates to display.
|
| 23 |
+
|
| 24 |
+
Returns:
|
| 25 |
+
True if query successful, False otherwise.
|
| 26 |
+
"""
|
| 27 |
+
print("--- 🧾 Checking Existing Candidates ---")
|
| 28 |
+
session = SessionLocal()
|
| 29 |
+
try:
|
| 30 |
+
count = session.query(Candidate).count()
|
| 31 |
+
print(f"📊 Found {count} candidate(s) in the database.")
|
| 32 |
+
|
| 33 |
+
if count == 0:
|
| 34 |
+
print("⚠️ No candidates found.")
|
| 35 |
+
else:
|
| 36 |
+
print(f"\n👀 Listing candidates (up to {limit}):")
|
| 37 |
+
candidates = (
|
| 38 |
+
session.query(Candidate)
|
| 39 |
+
.order_by(Candidate.full_name)
|
| 40 |
+
.limit(limit)
|
| 41 |
+
.all()
|
| 42 |
+
)
|
| 43 |
+
for c in candidates:
|
| 44 |
+
print(f" - {c.full_name} | {c.email} | Status: {c.status}")
|
| 45 |
+
|
| 46 |
+
return True
|
| 47 |
+
|
| 48 |
+
except ProgrammingError as e:
|
| 49 |
+
print("❌ Table 'candidates' does not exist or schema not initialized.")
|
| 50 |
+
print("ℹ️ Try running your DB initialization script or migrations.")
|
| 51 |
+
print(f"Error: {e}")
|
| 52 |
+
return False
|
| 53 |
+
except Exception as e:
|
| 54 |
+
print("❌ Error during candidate check.")
|
| 55 |
+
print(f"Error: {e}")
|
| 56 |
+
return False
|
| 57 |
+
finally:
|
| 58 |
+
session.close()
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
if __name__ == "__main__":
|
| 62 |
+
list_candidates()
|
| 63 |
+
|
scripts/db/test_connection.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test basic database connection.
|
| 3 |
+
|
| 4 |
+
Run standalone:
|
| 5 |
+
>>> POSTGRES_HOST=localhost POSTGRES_PORT=5433 python scripts/db/test_connection.py
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
from sqlalchemy import text
|
| 10 |
+
|
| 11 |
+
# Ensure project root is in path
|
| 12 |
+
import scripts.db # noqa: F401
|
| 13 |
+
|
| 14 |
+
from src.database.candidates.client import get_engine
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def test_connection() -> bool:
|
| 18 |
+
"""
|
| 19 |
+
Test basic database connectivity.
|
| 20 |
+
|
| 21 |
+
Returns:
|
| 22 |
+
True if connection successful, False otherwise.
|
| 23 |
+
"""
|
| 24 |
+
print("--- Testing Database Connection ---")
|
| 25 |
+
|
| 26 |
+
# Print environment info
|
| 27 |
+
print(f"POSTGRES_HOST (env): {os.environ.get('POSTGRES_HOST')}")
|
| 28 |
+
print(f"POSTGRES_PORT (env): {os.environ.get('POSTGRES_PORT')}")
|
| 29 |
+
|
| 30 |
+
try:
|
| 31 |
+
engine = get_engine()
|
| 32 |
+
print(f"Engine URL: {engine.url}")
|
| 33 |
+
|
| 34 |
+
with engine.connect() as connection:
|
| 35 |
+
print("✅ Connection successful!")
|
| 36 |
+
result = connection.execute(text("SELECT 1"))
|
| 37 |
+
print(f"✅ SELECT 1 result: {result.fetchone()}")
|
| 38 |
+
return True
|
| 39 |
+
|
| 40 |
+
except Exception as e:
|
| 41 |
+
print("\n❌ Connection FAILED")
|
| 42 |
+
print(f"Error type: {type(e).__name__}")
|
| 43 |
+
print(f"Error message: {str(e)}")
|
| 44 |
+
import traceback
|
| 45 |
+
traceback.print_exc()
|
| 46 |
+
return False
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
if __name__ == "__main__":
|
| 50 |
+
test_connection()
|
| 51 |
+
|
scripts/db/test_session.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test database session and query execution.
|
| 3 |
+
|
| 4 |
+
Run standalone:
|
| 5 |
+
>>> POSTGRES_HOST=localhost POSTGRES_PORT=5433 python scripts/db/test_session.py
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from sqlalchemy import text
|
| 9 |
+
|
| 10 |
+
# Ensure project root is in path
|
| 11 |
+
import scripts.db # noqa: F401
|
| 12 |
+
|
| 13 |
+
from src.database.candidates.client import SessionLocal
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def test_session_query() -> bool:
|
| 17 |
+
"""
|
| 18 |
+
Test session creation and basic query execution.
|
| 19 |
+
|
| 20 |
+
Returns:
|
| 21 |
+
True if session works, False otherwise.
|
| 22 |
+
"""
|
| 23 |
+
print("--- Testing Session Query ---")
|
| 24 |
+
session = SessionLocal()
|
| 25 |
+
try:
|
| 26 |
+
result = session.execute(text("SELECT now()"))
|
| 27 |
+
print(f"✅ Session execute successful: {result.fetchone()[0]}")
|
| 28 |
+
return True
|
| 29 |
+
|
| 30 |
+
except Exception as e:
|
| 31 |
+
print("\n❌ Session Query FAILED")
|
| 32 |
+
print(f"Error: {e}")
|
| 33 |
+
return False
|
| 34 |
+
finally:
|
| 35 |
+
session.close()
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
if __name__ == "__main__":
|
| 39 |
+
test_session_query()
|
| 40 |
+
|