Skip to main content

openrecruiter

The recruiting engine behind Open Recruiter: parse resumes and job descriptions, retrieve and rank candidates, draft outreach, and run an agent that does all of it through tools.

The desktop app is a consumer of this package, so everything shipped here is exercised by a real application rather than only by its own tests.

Install

Straight from the repository:

pip install "git+https://github.com/miao4ai/open_recruiter.git#subdirectory=sdk/core"

Or a released wheel, from the Releases page:

pip install https://github.com/miao4ai/open_recruiter/releases/download/sdk-core-v0.1.0/openrecruiter-0.1.0-py3-none-any.whl

Not on PyPI yet, so plain pip install openrecruiter will not find it. GitHub Packages has no Python registry, which is why the wheel is attached to a Release rather than appearing in the repository's Packages panel.

No local model is downloaded, at import or at runtime. Embeddings are an API call and chat is a hosted provider, so it runs on CPU, on macOS, and in a container with no GPU — 97 packages installed, none of them a training stack.

Quick start

from openrecruiter import Recruiter

r = Recruiter(anthropic_api_key="sk-ant-...", voyage_api_key="pa-...")

job = r.add_job(open("jd.txt").read())          # parsed into title, skills, requirements
r.add_candidate(open("resume.txt").read())      # parsed into a structured profile

for match in r.rank(job.id, top_k=10):
    print(f"{match.score:.2f}  {match.candidate_id}  {match.reasoning}")

Without a Voyage key, retrieval is disabled and ranking falls back to the LLM — the package still works, it just reads every candidate instead of shortlisting first.

The agent

The model is given real tools and decides what to call and when it is done, so one request can span several steps: rank a job, read the result, then draft the emails.

for event in r.chat("who are the three strongest fits for the CUDA role, and draft an intro to each"):
    match event:
        case TextDelta():        print(event.text, end="", flush=True)
        case ToolCall():         print(f"\n[{event.name}]")
        case ApprovalRequired(): ...   # a gated tool is waiting for a human

Events are TextDelta, ToolCall, ToolResult, ApprovalRequired, and Finished. Text streams as it is generated; tool calls are only emitted once their arguments are complete.

For a one-liner, r.ask("...") returns just the final text.

Approval gates

A tool marked requires_approval stops the run rather than acting. Anything the model queued behind it is held too, so the gate cannot be stepped around:

events = list(r.chat("email the top candidate"))
if agent.pending:                                   # serialisable — store it, decide later
    list(agent.resume(agent.pending, approved=True))

Your own tools

from openrecruiter import Tool

check_calendar = Tool(
    name="check_calendar",
    description="Look at the recruiter's availability this week",
    parameters={"type": "object", "properties": {"days": {"type": "integer"}}},
    fn=lambda days=7: my_calendar.free_slots(days),
)

r = Recruiter(anthropic_api_key="...", extra_tools=[check_calendar])

Ranking

Ranking is the main extension point. One interface, several backends:

Ranker
├── EmbeddingRanker   vector similarity — the default, cheap enough for the whole pool
├── APIRanker         an LLM scores each candidate and explains itself
├── TwoStageRanker    retrieve with one, rerank the shortlist with the other
└── your own          implement rank(job, candidates, top_k) and pass it in

TwoStageRanker is the shape the ranking research targets — the first stage optimises recall, the second optimises relevance over a few hundred candidates:

from openrecruiter import APIRanker, EmbeddingRanker, TwoStageRanker

r.ranker = TwoStageRanker(
    EmbeddingRanker(r.index),
    APIRanker(r.llm),
    shortlist=200,
)

Backends that need a local model live in their own distributions — recruitgpt for the distilled ranker, openrecruiter-fairness for bias-aware reranking — so nothing heavy reaches this install. Both must lazy-load: no download until a user selects that backend.

Bringing your own storage

Store and VectorIndex are protocols. Implement them over a database you already have and nothing above the storage layer changes — that is how the desktop app keeps its existing schema while running on this package.

class MyStore:
    def add_job(self, job): ...
    def get_job(self, job_id): ...
    def list_jobs(self, limit=100): ...
    # ... six more, all in openrecruiter.store.base

r = Recruiter(config, store=MyStore())

The default SQLiteStore is a working reference in four tables.

Development

cd sdk/core
uv sync
uv run pytest              # no network calls: the LLM is faked end to end
uv build --out-dir dist

Releasing: bump version in pyproject.toml, then push a matching tag.

git tag sdk-core-v0.1.1 && git push origin sdk-core-v0.1.1

CI checks the tag against the version, runs the tests, builds, installs the wheel in a clean environment and imports it, verifies no training stack came along, and attaches the artifacts to a Release.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openrecruiter-0.1.0.tar.gz (38.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openrecruiter-0.1.0-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

File details

Details for the file openrecruiter-0.1.0.tar.gz.

File metadata

  • Download URL: openrecruiter-0.1.0.tar.gz
  • Upload date:
  • Size: 38.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for openrecruiter-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bd6902f477aa7591deed79b7d7081314ae4cfc5cd216cbe2d618cb595daa834a
MD5 26d2821c5bbaf2112334796fccce5643
BLAKE2b-256 86edd9201d3f27b34ec9631f85d39e41e87602d794d1c6582118182d63255f6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for openrecruiter-0.1.0.tar.gz:

Publisher: publish-sdk.yml on miao4ai/open_recruiter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openrecruiter-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: openrecruiter-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 37.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for openrecruiter-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 44f0abba90898ff1ac58de8cc72f4332d918114ecae23971541b26750febd813
MD5 85da698a39f71fc1df8e390d176c5cd9
BLAKE2b-256 2a00be3e450cd461bd7f2ff8ed83d1e7c9caec01ef8f1bd00fa72539993445fe

See more details on using hashes here.

Provenance

The following attestation bundles were made for openrecruiter-0.1.0-py3-none-any.whl:

Publisher: publish-sdk.yml on miao4ai/open_recruiter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page