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.
- carejobs_haystack-0.1.0/.gitignore +17 -0
- carejobs_haystack-0.1.0/CHANGELOG.md +5 -0
- carejobs_haystack-0.1.0/LICENSE +21 -0
- carejobs_haystack-0.1.0/PKG-INFO +210 -0
- carejobs_haystack-0.1.0/README.md +183 -0
- carejobs_haystack-0.1.0/pyproject.toml +153 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/__init__.py +8 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/job_search.py +236 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/lookup.py +105 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/components/retrievers/carejobs/py.typed +0 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/tools/carejobs/__init__.py +7 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/tools/carejobs/py.typed +0 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/tools/carejobs/toolset.py +107 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/__init__.py +28 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/client.py +234 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/py.typed +0 -0
- carejobs_haystack-0.1.0/src/haystack_integrations/utils/carejobs/schema.py +223 -0
- carejobs_haystack-0.1.0/tests/__init__.py +0 -0
- carejobs_haystack-0.1.0/tests/helpers.py +89 -0
- carejobs_haystack-0.1.0/tests/test_agent_and_pipeline.py +133 -0
- carejobs_haystack-0.1.0/tests/test_client.py +126 -0
- carejobs_haystack-0.1.0/tests/test_job_search.py +107 -0
- carejobs_haystack-0.1.0/tests/test_live.py +96 -0
- carejobs_haystack-0.1.0/tests/test_lookup.py +52 -0
- carejobs_haystack-0.1.0/tests/test_toolset.py +88 -0
|
@@ -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
|
+
[](https://pypi.org/project/carejobs-haystack/#history)
|
|
31
|
+
[](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
|
+
[](https://pypi.org/project/carejobs-haystack/#history)
|
|
4
|
+
[](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"
|