carejobs-haystack 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. carejobs_haystack-0.1.0/.gitignore +17 -0
  2. carejobs_haystack-0.1.0/CHANGELOG.md +5 -0
  3. carejobs_haystack-0.1.0/LICENSE +21 -0
  4. carejobs_haystack-0.1.0/PKG-INFO +210 -0
  5. carejobs_haystack-0.1.0/README.md +183 -0
  6. carejobs_haystack-0.1.0/pyproject.toml +153 -0
  7. carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/__init__.py +8 -0
  8. carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/job_search.py +236 -0
  9. carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/lookup.py +105 -0
  10. carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/py.typed +0 -0
  11. carejobs_haystack-0.1.0/src/haystack_integrations/tools/carejobs/__init__.py +7 -0
  12. carejobs_haystack-0.1.0/src/haystack_integrations/tools/carejobs/py.typed +0 -0
  13. carejobs_haystack-0.1.0/src/haystack_integrations/tools/carejobs/toolset.py +107 -0
  14. carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/__init__.py +28 -0
  15. carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/client.py +234 -0
  16. carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/py.typed +0 -0
  17. carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/schema.py +223 -0
  18. carejobs_haystack-0.1.0/tests/__init__.py +0 -0
  19. carejobs_haystack-0.1.0/tests/helpers.py +89 -0
  20. carejobs_haystack-0.1.0/tests/test_agent_and_pipeline.py +133 -0
  21. carejobs_haystack-0.1.0/tests/test_client.py +126 -0
  22. carejobs_haystack-0.1.0/tests/test_job_search.py +107 -0
  23. carejobs_haystack-0.1.0/tests/test_live.py +96 -0
  24. carejobs_haystack-0.1.0/tests/test_lookup.py +52 -0
  25. carejobs_haystack-0.1.0/tests/test_toolset.py +88 -0
@@ -0,0 +1,17 @@
1
+ .DS_Store
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .env
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .mypy_cache/
13
+ .mypy_cache_test/
14
+ .coverage
15
+ htmlcov/
16
+ .idea/
17
+ .vscode/
@@ -0,0 +1,5 @@
1
+ # CHANGELOG
2
+
3
+ ## [0.1.0] - 2026-10-19
4
+
5
+ - Initial release: `CareJobsJobSearch` (one `Document` per open job), `CareJobsLookup` (readable summary and structured data of any CareJobs.Now tool) and `CareJobsToolset` (the six tools for Haystack Agents), over the public CareJobs.Now remote MCP endpoint. Sync and async.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CareJobs.Now
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,210 @@
1
+ Metadata-Version: 2.5
2
+ Name: carejobs-haystack
3
+ Version: 0.1.0
4
+ Summary: Haystack components and tools for CareJobs.Now: open US healthcare jobs, posted pay and employers
5
+ Project-URL: Homepage, https://carejobs.now
6
+ Project-URL: Documentation, https://github.com/yumaheymans/carejobs-haystack#readme
7
+ Project-URL: Issues, https://github.com/yumaheymans/carejobs-haystack/issues
8
+ Project-URL: Source, https://github.com/yumaheymans/carejobs-haystack
9
+ Project-URL: Changelog, https://github.com/yumaheymans/carejobs-haystack/blob/main/CHANGELOG.md
10
+ Author-email: "CareJobs.Now" <hello@carejobs.now>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: agent,carejobs,haystack,healthcare,jobs,mcp,nursing,tools
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: Implementation :: CPython
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: haystack-ai<4.0.0,>=3.0.0
25
+ Requires-Dist: httpx<1.0.0,>=0.27.0
26
+ Description-Content-Type: text/markdown
27
+
28
+ # carejobs-haystack
29
+
30
+ [![PyPI - Version](https://img.shields.io/pypi/v/carejobs-haystack?label=%20)](https://pypi.org/project/carejobs-haystack/#history)
31
+ [![PyPI - License](https://img.shields.io/pypi/l/carejobs-haystack)](https://opensource.org/licenses/MIT)
32
+
33
+ Haystack components and tools for [CareJobs.Now](https://carejobs.now), a free US healthcare job board.
34
+ It reads the open jobs of more than 2,000 hospitals, health systems, clinic groups and care companies:
35
+ nurses, CNAs, doctors, NPs and PAs, therapists, imaging and lab techs, pharmacy, caregivers, front
36
+ office and more.
37
+
38
+ The package gives a Haystack pipeline or Agent live access to those jobs, their posted pay, the
39
+ employers and the job-market numbers, through the public CareJobs.Now remote MCP endpoint. **No API key
40
+ or account is needed.** Every job links to its page on CareJobs.Now, where the person applies.
41
+
42
+ - [Installation](#installation)
43
+ - [Components](#components)
44
+ - [Usage](#usage)
45
+ - [Errors](#errors)
46
+ - [License](#license)
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install carejobs-haystack
52
+ ```
53
+
54
+ The package needs Python 3.11 or later and `haystack-ai` 3.0 or later.
55
+
56
+ ## Components
57
+
58
+ | Component | What it does |
59
+ | --- | --- |
60
+ | `CareJobsJobSearch` | Searches open jobs and returns one `Document` per job, with the job as `content` and its fields in `meta`. |
61
+ | `CareJobsLookup` | Calls one CareJobs.Now tool and returns its readable `text` and structured `data`: pay, job market, one state, the employer list, or one employer. |
62
+ | `CareJobsToolset` | The six CareJobs.Now tools as Haystack `Tool`s for an `Agent` or a chat generator. |
63
+
64
+ Import them from:
65
+
66
+ ```python
67
+ from haystack_integrations.components.retrievers.carejobs import CareJobsJobSearch, CareJobsLookup
68
+ from haystack_integrations.tools.carejobs import CareJobsToolset
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ ### Search jobs and get Documents
74
+
75
+ ```python
76
+ from haystack_integrations.components.retrievers.carejobs import CareJobsJobSearch
77
+
78
+ search = CareJobsJobSearch(kind="nurses", state="TX", top_k=3)
79
+ result = search.run(query="ICU")
80
+
81
+ print(result["total"]) # how many open jobs match
82
+ for document in result["documents"]:
83
+ print(document.meta["title"], "|", document.meta["employer"], "|", document.meta["url"])
84
+ ```
85
+
86
+ Each `Document.meta` holds `title`, `employer`, `kind`, `pay`, `schedule`, `facility`, `location`,
87
+ `listed`, `url` (the job's page, where the person applies), `employer_page`, `via` and `source`.
88
+ `run()` takes `query`, `kind`, `employer`, `state`, `remote`, `min_hourly_usd`, `sort` and `top_k`, and
89
+ `run_async()` takes the same.
90
+
91
+ `kind` is one of `nurses`, `nurse-practitioners`, `physicians`, `nursing-assistants`, `caregivers`,
92
+ `therapy-rehab`, `mental-health`, `pharmacy`, `imaging`, `lab`, `surgical-techs`, `emts-paramedics`,
93
+ `medical-assistants`, `dental`, `admin`, `allied-health` or `ai-training`. `state` is a two-letter code
94
+ (`TX`) or a name (`Texas`). `employer` is a slug such as `kaiser-permanente`, or a name.
95
+
96
+ ### Use the jobs in a pipeline
97
+
98
+ ```python
99
+ from haystack import Pipeline
100
+ from haystack.components.builders import PromptBuilder
101
+ from haystack_integrations.components.retrievers.carejobs import CareJobsJobSearch
102
+
103
+ pipeline = Pipeline()
104
+ pipeline.add_component("jobs", CareJobsJobSearch(kind="nursing-assistants", state="OH", top_k=3))
105
+ pipeline.add_component(
106
+ "prompt",
107
+ PromptBuilder(
108
+ template="""Open jobs:
109
+ {% for doc in documents %}{{ doc.content }}
110
+
111
+ {% endfor %}
112
+ Summarize the best paying job for {{ question }}""",
113
+ required_variables=["documents", "question"],
114
+ ),
115
+ )
116
+ pipeline.connect("jobs.documents", "prompt.documents")
117
+
118
+ result = pipeline.run({"jobs": {"query": "night"}, "prompt": {"question": "a night-shift CNA"}})
119
+ print(result["prompt"]["prompt"])
120
+ ```
121
+
122
+ Connect `prompt` to a chat generator to get an answer.
123
+
124
+ ### Look up pay, the job market, a state or an employer
125
+
126
+ ```python
127
+ from haystack_integrations.components.retrievers.carejobs import CareJobsLookup
128
+
129
+ pay = CareJobsLookup(tool_name="get_healthcare_pay")
130
+ result = pay.run(arguments={"kind": "nursing-assistants"})
131
+ print(result["text"]) # the readable summary
132
+ print(result["data"]) # the structured answer (a dict)
133
+
134
+ employer = CareJobsLookup(tool_name="get_healthcare_employer")
135
+ print(employer.run(arguments={"employer": "kaiser-permanente"})["text"])
136
+ ```
137
+
138
+ `tool_name` is one of `search_healthcare_jobs`, `get_healthcare_pay`, `get_healthcare_job_market`,
139
+ `get_healthcare_jobs_by_state`, `list_healthcare_employers` or `get_healthcare_employer`.
140
+
141
+ ### Give an Agent the tools
142
+
143
+ ```python
144
+ from haystack.components.agents import Agent
145
+ from haystack.components.generators.chat import OpenAIChatGenerator
146
+ from haystack.dataclasses import ChatMessage
147
+ from haystack_integrations.tools.carejobs import CareJobsToolset
148
+
149
+ agent = Agent(chat_generator=OpenAIChatGenerator(), tools=CareJobsToolset())
150
+
151
+ result = agent.run(
152
+ messages=[
153
+ ChatMessage.from_user(
154
+ "Find night-shift ICU nursing jobs in Texas that pay at least $45 an hour, "
155
+ "and tell me which employers post the most for nurses."
156
+ )
157
+ ]
158
+ )
159
+ print(result["last_message"].text)
160
+ ```
161
+
162
+ The toolset has `search_healthcare_jobs`, `get_healthcare_pay`, `get_healthcare_job_market`,
163
+ `get_healthcare_jobs_by_state`, `list_healthcare_employers` and `get_healthcare_employer`. Each tool
164
+ returns the service's readable summary with a link to every job. Tools run natively async.
165
+
166
+ ## Errors
167
+
168
+ If CareJobs.Now cannot be reached or rejects a request (for example an unknown employer), the
169
+ components raise `CareJobsError` with the service's message. Import it from
170
+ `haystack_integrations.utils.carejobs`.
171
+
172
+ A Haystack `Tool` wraps any error in `ToolInvocationError`, whose message contains the service's
173
+ message and whose `__cause__` is the `CareJobsError`. A Haystack `Agent` hands that message back to the
174
+ model (unless `raise_on_tool_invocation_failure=True`), which can correct the call.
175
+
176
+ The first request after a quiet period can take up to about 25 seconds while the service wakes. The
177
+ default timeout is 30 seconds. To wait longer, pass `timeout=60` to a component or the toolset.
178
+
179
+ ## Serialization
180
+
181
+ Components and the toolset serialize with `to_dict()` and `from_dict()`, so pipelines and Agents that
182
+ use them save and load with `dumps()` and `loads()`. The `haystack_integrations` package is on
183
+ Haystack's default allowlist for deserialization, so no extra setting is needed.
184
+
185
+ ## Notes on the data
186
+
187
+ - Pay is what employers post, measured from the hourly rates open jobs state, before shift
188
+ differentials and bonuses.
189
+ - Results cover US jobs only.
190
+ - Send people to the link in each result to apply. The tools never apply to a job.
191
+
192
+ ## Privacy
193
+
194
+ The components send only the arguments of each call to `https://carejobs.now/api/mcp` and store
195
+ nothing. See the CareJobs.Now [privacy policy](https://carejobs.now/legal/privacy) and
196
+ [terms](https://carejobs.now/legal/terms).
197
+
198
+ ## Development
199
+
200
+ ```bash
201
+ pip install hatch
202
+ hatch run test:unit # unit tests, no network
203
+ hatch run test:integration # also needs network: calls the live API
204
+ hatch run fmt-check
205
+ hatch run test:types
206
+ ```
207
+
208
+ ## License
209
+
210
+ `carejobs-haystack` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
@@ -0,0 +1,183 @@
1
+ # carejobs-haystack
2
+
3
+ [![PyPI - Version](https://img.shields.io/pypi/v/carejobs-haystack?label=%20)](https://pypi.org/project/carejobs-haystack/#history)
4
+ [![PyPI - License](https://img.shields.io/pypi/l/carejobs-haystack)](https://opensource.org/licenses/MIT)
5
+
6
+ Haystack components and tools for [CareJobs.Now](https://carejobs.now), a free US healthcare job board.
7
+ It reads the open jobs of more than 2,000 hospitals, health systems, clinic groups and care companies:
8
+ nurses, CNAs, doctors, NPs and PAs, therapists, imaging and lab techs, pharmacy, caregivers, front
9
+ office and more.
10
+
11
+ The package gives a Haystack pipeline or Agent live access to those jobs, their posted pay, the
12
+ employers and the job-market numbers, through the public CareJobs.Now remote MCP endpoint. **No API key
13
+ or account is needed.** Every job links to its page on CareJobs.Now, where the person applies.
14
+
15
+ - [Installation](#installation)
16
+ - [Components](#components)
17
+ - [Usage](#usage)
18
+ - [Errors](#errors)
19
+ - [License](#license)
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install carejobs-haystack
25
+ ```
26
+
27
+ The package needs Python 3.11 or later and `haystack-ai` 3.0 or later.
28
+
29
+ ## Components
30
+
31
+ | Component | What it does |
32
+ | --- | --- |
33
+ | `CareJobsJobSearch` | Searches open jobs and returns one `Document` per job, with the job as `content` and its fields in `meta`. |
34
+ | `CareJobsLookup` | Calls one CareJobs.Now tool and returns its readable `text` and structured `data`: pay, job market, one state, the employer list, or one employer. |
35
+ | `CareJobsToolset` | The six CareJobs.Now tools as Haystack `Tool`s for an `Agent` or a chat generator. |
36
+
37
+ Import them from:
38
+
39
+ ```python
40
+ from haystack_integrations.components.retrievers.carejobs import CareJobsJobSearch, CareJobsLookup
41
+ from haystack_integrations.tools.carejobs import CareJobsToolset
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ ### Search jobs and get Documents
47
+
48
+ ```python
49
+ from haystack_integrations.components.retrievers.carejobs import CareJobsJobSearch
50
+
51
+ search = CareJobsJobSearch(kind="nurses", state="TX", top_k=3)
52
+ result = search.run(query="ICU")
53
+
54
+ print(result["total"]) # how many open jobs match
55
+ for document in result["documents"]:
56
+ print(document.meta["title"], "|", document.meta["employer"], "|", document.meta["url"])
57
+ ```
58
+
59
+ Each `Document.meta` holds `title`, `employer`, `kind`, `pay`, `schedule`, `facility`, `location`,
60
+ `listed`, `url` (the job's page, where the person applies), `employer_page`, `via` and `source`.
61
+ `run()` takes `query`, `kind`, `employer`, `state`, `remote`, `min_hourly_usd`, `sort` and `top_k`, and
62
+ `run_async()` takes the same.
63
+
64
+ `kind` is one of `nurses`, `nurse-practitioners`, `physicians`, `nursing-assistants`, `caregivers`,
65
+ `therapy-rehab`, `mental-health`, `pharmacy`, `imaging`, `lab`, `surgical-techs`, `emts-paramedics`,
66
+ `medical-assistants`, `dental`, `admin`, `allied-health` or `ai-training`. `state` is a two-letter code
67
+ (`TX`) or a name (`Texas`). `employer` is a slug such as `kaiser-permanente`, or a name.
68
+
69
+ ### Use the jobs in a pipeline
70
+
71
+ ```python
72
+ from haystack import Pipeline
73
+ from haystack.components.builders import PromptBuilder
74
+ from haystack_integrations.components.retrievers.carejobs import CareJobsJobSearch
75
+
76
+ pipeline = Pipeline()
77
+ pipeline.add_component("jobs", CareJobsJobSearch(kind="nursing-assistants", state="OH", top_k=3))
78
+ pipeline.add_component(
79
+ "prompt",
80
+ PromptBuilder(
81
+ template="""Open jobs:
82
+ {% for doc in documents %}{{ doc.content }}
83
+
84
+ {% endfor %}
85
+ Summarize the best paying job for {{ question }}""",
86
+ required_variables=["documents", "question"],
87
+ ),
88
+ )
89
+ pipeline.connect("jobs.documents", "prompt.documents")
90
+
91
+ result = pipeline.run({"jobs": {"query": "night"}, "prompt": {"question": "a night-shift CNA"}})
92
+ print(result["prompt"]["prompt"])
93
+ ```
94
+
95
+ Connect `prompt` to a chat generator to get an answer.
96
+
97
+ ### Look up pay, the job market, a state or an employer
98
+
99
+ ```python
100
+ from haystack_integrations.components.retrievers.carejobs import CareJobsLookup
101
+
102
+ pay = CareJobsLookup(tool_name="get_healthcare_pay")
103
+ result = pay.run(arguments={"kind": "nursing-assistants"})
104
+ print(result["text"]) # the readable summary
105
+ print(result["data"]) # the structured answer (a dict)
106
+
107
+ employer = CareJobsLookup(tool_name="get_healthcare_employer")
108
+ print(employer.run(arguments={"employer": "kaiser-permanente"})["text"])
109
+ ```
110
+
111
+ `tool_name` is one of `search_healthcare_jobs`, `get_healthcare_pay`, `get_healthcare_job_market`,
112
+ `get_healthcare_jobs_by_state`, `list_healthcare_employers` or `get_healthcare_employer`.
113
+
114
+ ### Give an Agent the tools
115
+
116
+ ```python
117
+ from haystack.components.agents import Agent
118
+ from haystack.components.generators.chat import OpenAIChatGenerator
119
+ from haystack.dataclasses import ChatMessage
120
+ from haystack_integrations.tools.carejobs import CareJobsToolset
121
+
122
+ agent = Agent(chat_generator=OpenAIChatGenerator(), tools=CareJobsToolset())
123
+
124
+ result = agent.run(
125
+ messages=[
126
+ ChatMessage.from_user(
127
+ "Find night-shift ICU nursing jobs in Texas that pay at least $45 an hour, "
128
+ "and tell me which employers post the most for nurses."
129
+ )
130
+ ]
131
+ )
132
+ print(result["last_message"].text)
133
+ ```
134
+
135
+ The toolset has `search_healthcare_jobs`, `get_healthcare_pay`, `get_healthcare_job_market`,
136
+ `get_healthcare_jobs_by_state`, `list_healthcare_employers` and `get_healthcare_employer`. Each tool
137
+ returns the service's readable summary with a link to every job. Tools run natively async.
138
+
139
+ ## Errors
140
+
141
+ If CareJobs.Now cannot be reached or rejects a request (for example an unknown employer), the
142
+ components raise `CareJobsError` with the service's message. Import it from
143
+ `haystack_integrations.utils.carejobs`.
144
+
145
+ A Haystack `Tool` wraps any error in `ToolInvocationError`, whose message contains the service's
146
+ message and whose `__cause__` is the `CareJobsError`. A Haystack `Agent` hands that message back to the
147
+ model (unless `raise_on_tool_invocation_failure=True`), which can correct the call.
148
+
149
+ The first request after a quiet period can take up to about 25 seconds while the service wakes. The
150
+ default timeout is 30 seconds. To wait longer, pass `timeout=60` to a component or the toolset.
151
+
152
+ ## Serialization
153
+
154
+ Components and the toolset serialize with `to_dict()` and `from_dict()`, so pipelines and Agents that
155
+ use them save and load with `dumps()` and `loads()`. The `haystack_integrations` package is on
156
+ Haystack's default allowlist for deserialization, so no extra setting is needed.
157
+
158
+ ## Notes on the data
159
+
160
+ - Pay is what employers post, measured from the hourly rates open jobs state, before shift
161
+ differentials and bonuses.
162
+ - Results cover US jobs only.
163
+ - Send people to the link in each result to apply. The tools never apply to a job.
164
+
165
+ ## Privacy
166
+
167
+ The components send only the arguments of each call to `https://carejobs.now/api/mcp` and store
168
+ nothing. See the CareJobs.Now [privacy policy](https://carejobs.now/legal/privacy) and
169
+ [terms](https://carejobs.now/legal/terms).
170
+
171
+ ## Development
172
+
173
+ ```bash
174
+ pip install hatch
175
+ hatch run test:unit # unit tests, no network
176
+ hatch run test:integration # also needs network: calls the live API
177
+ hatch run fmt-check
178
+ hatch run test:types
179
+ ```
180
+
181
+ ## License
182
+
183
+ `carejobs-haystack` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
@@ -0,0 +1,153 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "carejobs-haystack"
7
+ version = "0.1.0"
8
+ description = "Haystack components and tools for CareJobs.Now: open US healthcare jobs, posted pay and employers"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ keywords = ["haystack", "carejobs", "jobs", "healthcare", "nursing", "agent", "tools", "mcp"]
14
+ authors = [{ name = "CareJobs.Now", email = "hello@carejobs.now" }]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: Implementation :: CPython",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = ["haystack-ai>=3.0.0,<4.0.0", "httpx>=0.27.0,<1.0.0"]
27
+
28
+ [project.urls]
29
+ Homepage = "https://carejobs.now"
30
+ Documentation = "https://github.com/yumaheymans/carejobs-haystack#readme"
31
+ Issues = "https://github.com/yumaheymans/carejobs-haystack/issues"
32
+ Source = "https://github.com/yumaheymans/carejobs-haystack"
33
+ Changelog = "https://github.com/yumaheymans/carejobs-haystack/blob/main/CHANGELOG.md"
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ packages = ["src/haystack_integrations"]
37
+
38
+ [tool.hatch.build.targets.sdist]
39
+ include = ["src/", "tests/", "README.md", "LICENSE", "CHANGELOG.md"]
40
+
41
+ [tool.hatch.envs.default]
42
+ installer = "uv"
43
+ dependencies = ["ruff"]
44
+
45
+ [tool.hatch.envs.default.scripts]
46
+ fmt = "ruff check --fix {args}; ruff format {args}"
47
+ fmt-check = "ruff check {args} && ruff format --check {args}"
48
+
49
+ [tool.hatch.envs.test]
50
+ dependencies = ["pytest", "pytest-asyncio", "pytest-cov", "mypy", "codespell"]
51
+
52
+ [tool.hatch.envs.test.scripts]
53
+ unit = 'pytest -m "not integration" {args:tests}'
54
+ integration = 'pytest -m "integration" {args:tests}'
55
+ all = 'pytest {args:tests}'
56
+ cov = 'pytest --cov=haystack_integrations -m "not integration" {args:tests}'
57
+ types = "mypy -p haystack_integrations.utils.carejobs -p haystack_integrations.components.retrievers.carejobs -p haystack_integrations.tools.carejobs {args}"
58
+ spell = "codespell src tests README.md CHANGELOG.md"
59
+
60
+ [tool.mypy]
61
+ check_untyped_defs = true
62
+ disallow_incomplete_defs = true
63
+ disallow_untyped_defs = true
64
+ python_version = "3.11"
65
+
66
+ [tool.codespell]
67
+ ignore-words-list = "cna,cnas"
68
+
69
+ [tool.ruff]
70
+ line-length = 120
71
+ target-version = "py311"
72
+
73
+ [tool.ruff.lint]
74
+ select = [
75
+ "A",
76
+ "ANN",
77
+ "ARG",
78
+ "B",
79
+ "C",
80
+ "D102", # Missing docstring in public method
81
+ "D103", # Missing docstring in public function
82
+ "D205", # 1 blank line required between summary line and description
83
+ "D209", # Closing triple quotes go to new line
84
+ "D213", # summary lines must be positioned on the second physical line of the docstring
85
+ "D417", # Missing argument descriptions in the docstring
86
+ "D419", # Docstring is empty
87
+ "DTZ",
88
+ "E",
89
+ "EM",
90
+ "F",
91
+ "I",
92
+ "ICN",
93
+ "ISC",
94
+ "N",
95
+ "PLC",
96
+ "PLE",
97
+ "PLR",
98
+ "PLW",
99
+ "Q",
100
+ "RUF",
101
+ "S",
102
+ "T",
103
+ "TID",
104
+ "UP",
105
+ "W",
106
+ "YTT",
107
+ ]
108
+ ignore = [
109
+ # Allow non-abstract empty methods in abstract base classes
110
+ "B027",
111
+ # Allow function calls in argument defaults (common Haystack pattern for Secret.from_env_var)
112
+ "B008",
113
+ # Ignore checks for possible passwords
114
+ "S105",
115
+ "S106",
116
+ "S107",
117
+ # Ignore complexity
118
+ "C901",
119
+ "PLR0911",
120
+ "PLR0912",
121
+ "PLR0913",
122
+ "PLR0915",
123
+ "PLR0917",
124
+ # Allow `Any` type - used legitimately for dynamic types and SDK boundaries
125
+ "ANN401",
126
+ ]
127
+
128
+ [tool.ruff.lint.isort]
129
+ known-first-party = ["haystack_integrations"]
130
+
131
+ [tool.ruff.lint.flake8-tidy-imports]
132
+ ban-relative-imports = "parents"
133
+
134
+ [tool.ruff.lint.per-file-ignores]
135
+ # Tests can use magic values, assertions, relative imports, and don't need type annotations
136
+ "tests/**/*" = ["PLR2004", "S101", "TID252", "D", "ANN", "ARG", "EM", "SLF001"]
137
+
138
+ [tool.coverage.run]
139
+ source = ["haystack_integrations"]
140
+ branch = true
141
+ parallel = false
142
+ relative_files = true
143
+
144
+ [tool.coverage.report]
145
+ omit = ["*/tests/*", "*/__init__.py"]
146
+ show_missing = true
147
+ exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]
148
+
149
+ [tool.pytest.ini_options]
150
+ addopts = "--strict-markers"
151
+ markers = ["integration: calls the live CareJobs.Now API (run with -m integration)"]
152
+ asyncio_mode = "auto"
153
+ asyncio_default_fixture_loop_scope = "function"
@@ -0,0 +1,8 @@
1
+ # SPDX-FileCopyrightText: 2026-present CareJobs.Now <hello@carejobs.now>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ from .job_search import CareJobsJobSearch
6
+ from .lookup import CareJobsLookup
7
+
8
+ __all__ = ["CareJobsJobSearch", "CareJobsLookup"]