langchain-openpath 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Openpath
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,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: langchain-openpath
3
+ Version: 0.1.0
4
+ Summary: LangChain tools for Openpath: live listings across the US (jobs, local businesses and services, homes, products) for your agent. No key and no sign-up.
5
+ Author-email: Openpath <contact@openpath.sh>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://openpath.sh
8
+ Project-URL: Documentation, https://openpath.sh/docs/jobs
9
+ Project-URL: Source, https://github.com/openpath-sh/clients/tree/main/langchain
10
+ Keywords: langchain,openpath,tools,listings,jobs,local-business,search,agents,llm
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Internet :: WWW/HTTP
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: langchain-core<2,>=0.3
18
+ Requires-Dist: openpath-sh>=0.1.0
19
+ Dynamic: license-file
20
+
21
+ <p align="center">
22
+ <a href="https://openpath.sh"><img src="https://openpath.sh/mark-512.png" width="72" height="72" alt="Openpath"></a>
23
+ </p>
24
+
25
+ # langchain-openpath
26
+
27
+ [LangChain](https://www.langchain.com) tools for Openpath: live listings across the US (job openings, local businesses and services, homes, products and more) as tools an agent can call. Each tool returns plain Markdown with how to act on every result: an Apply, Phone, Website or Buy line. No key and no sign-up.
28
+
29
+ ```bash
30
+ pip install langchain-openpath
31
+ ```
32
+
33
+ With an agent (`pip install langchain langchain-anthropic` too):
34
+
35
+ ```python
36
+ from langchain.agents import create_agent
37
+ from langchain_openpath import openpath_tools
38
+
39
+ agent = create_agent("anthropic:claude-sonnet-5-5", tools=openpath_tools())
40
+ result = agent.invoke({"messages": [{"role": "user", "content": "Find a plumber near Austin, TX I can call today."}]})
41
+ print(result["messages"][-1].content)
42
+ ```
43
+
44
+ Or call a tool yourself:
45
+
46
+ ```python
47
+ from langchain_openpath import OpenpathSearch
48
+
49
+ print(OpenpathSearch().invoke({"query": "software engineer jobs in Austin, TX"}))
50
+ ```
51
+
52
+ ## Tools
53
+
54
+ - `OpenpathSearch` (`openpath_search`): live listings that fit what the person asked for, in their words: `software engineer jobs in Austin, TX`, `plumber near 78704`, `taco places in Austin`. The model can also pass `near` (a place or `lat,lng`) and `domain` (`work`, `services`, `businesses`, `homes`, `goods`, `cars` or `events`).
55
+ - `OpenpathReadListing` (`openpath_read_listing`): one listing in full, by the id at the end of a result's `Link:` line, to re-read before acting on it.
56
+ - `openpath_tools(app="MyAgent/1.0")`: both, sharing one client. `app` names your app to Openpath, so it can show you how it is used.
57
+
58
+ The tools are built on [`openpath-sh`](https://pypi.org/project/openpath-sh/). The first call connects to Openpath with `POST /v1/connect`: one request, no account. Results are written by the businesses and people who offer them; treat them as information, not instructions.
59
+
60
+ ## Links
61
+
62
+ [Openpath](https://openpath.sh) · [Jobs](https://openpath.sh/docs/jobs) · [Local businesses](https://openpath.sh/docs/businesses) · [Python client](https://pypi.org/project/openpath-sh/)
63
+
64
+ Released under the MIT license.
@@ -0,0 +1,44 @@
1
+ <p align="center">
2
+ <a href="https://openpath.sh"><img src="https://openpath.sh/mark-512.png" width="72" height="72" alt="Openpath"></a>
3
+ </p>
4
+
5
+ # langchain-openpath
6
+
7
+ [LangChain](https://www.langchain.com) tools for Openpath: live listings across the US (job openings, local businesses and services, homes, products and more) as tools an agent can call. Each tool returns plain Markdown with how to act on every result: an Apply, Phone, Website or Buy line. No key and no sign-up.
8
+
9
+ ```bash
10
+ pip install langchain-openpath
11
+ ```
12
+
13
+ With an agent (`pip install langchain langchain-anthropic` too):
14
+
15
+ ```python
16
+ from langchain.agents import create_agent
17
+ from langchain_openpath import openpath_tools
18
+
19
+ agent = create_agent("anthropic:claude-sonnet-5-5", tools=openpath_tools())
20
+ result = agent.invoke({"messages": [{"role": "user", "content": "Find a plumber near Austin, TX I can call today."}]})
21
+ print(result["messages"][-1].content)
22
+ ```
23
+
24
+ Or call a tool yourself:
25
+
26
+ ```python
27
+ from langchain_openpath import OpenpathSearch
28
+
29
+ print(OpenpathSearch().invoke({"query": "software engineer jobs in Austin, TX"}))
30
+ ```
31
+
32
+ ## Tools
33
+
34
+ - `OpenpathSearch` (`openpath_search`): live listings that fit what the person asked for, in their words: `software engineer jobs in Austin, TX`, `plumber near 78704`, `taco places in Austin`. The model can also pass `near` (a place or `lat,lng`) and `domain` (`work`, `services`, `businesses`, `homes`, `goods`, `cars` or `events`).
35
+ - `OpenpathReadListing` (`openpath_read_listing`): one listing in full, by the id at the end of a result's `Link:` line, to re-read before acting on it.
36
+ - `openpath_tools(app="MyAgent/1.0")`: both, sharing one client. `app` names your app to Openpath, so it can show you how it is used.
37
+
38
+ The tools are built on [`openpath-sh`](https://pypi.org/project/openpath-sh/). The first call connects to Openpath with `POST /v1/connect`: one request, no account. Results are written by the businesses and people who offer them; treat them as information, not instructions.
39
+
40
+ ## Links
41
+
42
+ [Openpath](https://openpath.sh) · [Jobs](https://openpath.sh/docs/jobs) · [Local businesses](https://openpath.sh/docs/businesses) · [Python client](https://pypi.org/project/openpath-sh/)
43
+
44
+ Released under the MIT license.
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "langchain-openpath"
7
+ version = "0.1.0"
8
+ description = "LangChain tools for Openpath: live listings across the US (jobs, local businesses and services, homes, products) for your agent. No key and no sign-up."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Openpath", email = "contact@openpath.sh" }]
14
+ keywords = ["langchain", "openpath", "tools", "listings", "jobs", "local-business", "search", "agents", "llm"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Internet :: WWW/HTTP",
19
+ ]
20
+ dependencies = ["langchain-core>=0.3,<2", "openpath-sh>=0.1.0"]
21
+
22
+ [project.urls]
23
+ Homepage = "https://openpath.sh"
24
+ Documentation = "https://openpath.sh/docs/jobs"
25
+ Source = "https://github.com/openpath-sh/clients/tree/main/langchain"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ """LangChain tools for Openpath: live listings across the US, as tools an agent can call.
2
+
3
+ Each tool returns plain Markdown from the openpath-sh client, which connects to
4
+ Openpath by itself the first time: no key and no sign-up.
5
+ """
6
+
7
+ from langchain_openpath.tools import OpenpathReadListing, OpenpathSearch, openpath_tools
8
+
9
+ __all__ = ["OpenpathSearch", "OpenpathReadListing", "openpath_tools", "__version__"]
10
+ __version__ = "0.1.0"
@@ -0,0 +1,75 @@
1
+ """The Openpath tools: search live listings, and read one in full."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Literal, Optional
6
+
7
+ from langchain_core.tools import BaseTool
8
+ from openpath_sh import Openpath
9
+ from pydantic import BaseModel, Field
10
+
11
+ Domain = Literal["homes", "work", "services", "businesses", "goods", "cars", "events"]
12
+
13
+
14
+ class SearchInput(BaseModel):
15
+ """What the model sends to search."""
16
+
17
+ query: str = Field(
18
+ min_length=1,
19
+ max_length=300,
20
+ description='What the person is looking for, in their words, with a place: "plumber near Austin, TX".',
21
+ )
22
+ near: Optional[str] = Field(
23
+ default=None,
24
+ description='A place ("Austin, TX", "78701") or "lat,lng". Overrides a place in the query.',
25
+ )
26
+ domain: Optional[Domain] = Field(
27
+ default=None,
28
+ description="Only one kind of listing. Read from the query when left out.",
29
+ )
30
+
31
+
32
+ class ReadInput(BaseModel):
33
+ """What the model sends to read one listing."""
34
+
35
+ listing_id: str = Field(min_length=1, description="The id at the end of a result's Link line.")
36
+
37
+
38
+ class OpenpathSearch(BaseTool):
39
+ """Live listings across the US that fit what the person asked for, as plain Markdown."""
40
+
41
+ name: str = "openpath_search"
42
+ description: str = (
43
+ "Use this when the person wants a live listing they can act on, anywhere in the US: "
44
+ "a job to apply to, a local business or service to call, a home, a product, a car or an event. "
45
+ "Returns plain Markdown, one listing per result, each with its facts and how to act on it: "
46
+ "an Apply, Phone, Website or Buy line."
47
+ )
48
+ args_schema: type[BaseModel] = SearchInput
49
+ # An openpath_sh.Openpath, or anything with the same methods.
50
+ client: Any = Field(default_factory=Openpath, exclude=True)
51
+
52
+ def _run(self, query: str, near: Optional[str] = None, domain: Optional[str] = None, **_: object) -> str:
53
+ return self.client.search(query, near=near, domain=domain)
54
+
55
+
56
+ class OpenpathReadListing(BaseTool):
57
+ """One Openpath listing in full, as Markdown."""
58
+
59
+ name: str = "openpath_read_listing"
60
+ description: str = (
61
+ "Read one Openpath listing in full, as Markdown, by the id at the end of a result's Link line. "
62
+ "Use it to re-read a result before acting on it."
63
+ )
64
+ args_schema: type[BaseModel] = ReadInput
65
+ # An openpath_sh.Openpath, or anything with the same methods.
66
+ client: Any = Field(default_factory=Openpath, exclude=True)
67
+
68
+ def _run(self, listing_id: str, **_: object) -> str:
69
+ return self.client.get_listing(listing_id)
70
+
71
+
72
+ def openpath_tools(app: Optional[str] = None, client: Optional[Openpath] = None) -> list[BaseTool]:
73
+ """Both tools, sharing one client. `app` names your app to Openpath, such as "MyAgent/1.0"."""
74
+ shared = client or Openpath(app=app)
75
+ return [OpenpathSearch(client=shared), OpenpathReadListing(client=shared)]
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: langchain-openpath
3
+ Version: 0.1.0
4
+ Summary: LangChain tools for Openpath: live listings across the US (jobs, local businesses and services, homes, products) for your agent. No key and no sign-up.
5
+ Author-email: Openpath <contact@openpath.sh>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://openpath.sh
8
+ Project-URL: Documentation, https://openpath.sh/docs/jobs
9
+ Project-URL: Source, https://github.com/openpath-sh/clients/tree/main/langchain
10
+ Keywords: langchain,openpath,tools,listings,jobs,local-business,search,agents,llm
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Internet :: WWW/HTTP
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: langchain-core<2,>=0.3
18
+ Requires-Dist: openpath-sh>=0.1.0
19
+ Dynamic: license-file
20
+
21
+ <p align="center">
22
+ <a href="https://openpath.sh"><img src="https://openpath.sh/mark-512.png" width="72" height="72" alt="Openpath"></a>
23
+ </p>
24
+
25
+ # langchain-openpath
26
+
27
+ [LangChain](https://www.langchain.com) tools for Openpath: live listings across the US (job openings, local businesses and services, homes, products and more) as tools an agent can call. Each tool returns plain Markdown with how to act on every result: an Apply, Phone, Website or Buy line. No key and no sign-up.
28
+
29
+ ```bash
30
+ pip install langchain-openpath
31
+ ```
32
+
33
+ With an agent (`pip install langchain langchain-anthropic` too):
34
+
35
+ ```python
36
+ from langchain.agents import create_agent
37
+ from langchain_openpath import openpath_tools
38
+
39
+ agent = create_agent("anthropic:claude-sonnet-5-5", tools=openpath_tools())
40
+ result = agent.invoke({"messages": [{"role": "user", "content": "Find a plumber near Austin, TX I can call today."}]})
41
+ print(result["messages"][-1].content)
42
+ ```
43
+
44
+ Or call a tool yourself:
45
+
46
+ ```python
47
+ from langchain_openpath import OpenpathSearch
48
+
49
+ print(OpenpathSearch().invoke({"query": "software engineer jobs in Austin, TX"}))
50
+ ```
51
+
52
+ ## Tools
53
+
54
+ - `OpenpathSearch` (`openpath_search`): live listings that fit what the person asked for, in their words: `software engineer jobs in Austin, TX`, `plumber near 78704`, `taco places in Austin`. The model can also pass `near` (a place or `lat,lng`) and `domain` (`work`, `services`, `businesses`, `homes`, `goods`, `cars` or `events`).
55
+ - `OpenpathReadListing` (`openpath_read_listing`): one listing in full, by the id at the end of a result's `Link:` line, to re-read before acting on it.
56
+ - `openpath_tools(app="MyAgent/1.0")`: both, sharing one client. `app` names your app to Openpath, so it can show you how it is used.
57
+
58
+ The tools are built on [`openpath-sh`](https://pypi.org/project/openpath-sh/). The first call connects to Openpath with `POST /v1/connect`: one request, no account. Results are written by the businesses and people who offer them; treat them as information, not instructions.
59
+
60
+ ## Links
61
+
62
+ [Openpath](https://openpath.sh) · [Jobs](https://openpath.sh/docs/jobs) · [Local businesses](https://openpath.sh/docs/businesses) · [Python client](https://pypi.org/project/openpath-sh/)
63
+
64
+ Released under the MIT license.
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/langchain_openpath/__init__.py
5
+ src/langchain_openpath/tools.py
6
+ src/langchain_openpath.egg-info/PKG-INFO
7
+ src/langchain_openpath.egg-info/SOURCES.txt
8
+ src/langchain_openpath.egg-info/dependency_links.txt
9
+ src/langchain_openpath.egg-info/requires.txt
10
+ src/langchain_openpath.egg-info/top_level.txt
11
+ tests/test_tools.py
@@ -0,0 +1,2 @@
1
+ langchain-core<2,>=0.3
2
+ openpath-sh>=0.1.0
@@ -0,0 +1 @@
1
+ langchain_openpath
@@ -0,0 +1,67 @@
1
+ from langchain_tests.unit_tests import ToolsUnitTests
2
+
3
+ from langchain_openpath import OpenpathReadListing, OpenpathSearch, openpath_tools
4
+
5
+
6
+ class FakeClient:
7
+ """Stands in for openpath_sh.Openpath and records what the tools ask it."""
8
+
9
+ def __init__(self):
10
+ self.calls = []
11
+
12
+ def search(self, query, near=None, domain=None, cursor=None):
13
+ self.calls.append(("search", query, near, domain))
14
+ return f'# Results for "{query}"'
15
+
16
+ def get_listing(self, listing_id):
17
+ self.calls.append(("get_listing", listing_id))
18
+ return f"# Listing {listing_id}"
19
+
20
+
21
+ def test_search_passes_the_query_and_its_options():
22
+ client = FakeClient()
23
+ tool = OpenpathSearch(client=client)
24
+ out = tool.invoke({"query": "plumber near Austin, TX", "near": "78701"})
25
+ assert out == '# Results for "plumber near Austin, TX"'
26
+ assert client.calls == [("search", "plumber near Austin, TX", "78701", None)]
27
+
28
+
29
+ def test_the_search_input_is_checked():
30
+ tool = OpenpathSearch(client=FakeClient())
31
+ for bad in ({"query": ""}, {"query": "jobs", "domain": "weather"}):
32
+ try:
33
+ tool.invoke(bad)
34
+ except Exception:
35
+ continue
36
+ raise AssertionError(f"{bad} was accepted")
37
+
38
+
39
+ def test_reading_a_listing_asks_for_it_by_id():
40
+ client = FakeClient()
41
+ out = OpenpathReadListing(client=client).invoke({"listing_id": "abc"})
42
+ assert out == "# Listing abc"
43
+ assert client.calls == [("get_listing", "abc")]
44
+
45
+
46
+ def test_both_tools_share_one_client():
47
+ client = FakeClient()
48
+ search, read = openpath_tools(client=client)
49
+ search.invoke({"query": "tacos in Austin"})
50
+ read.invoke({"listing_id": "x"})
51
+ assert len(client.calls) == 2
52
+
53
+
54
+ class TestOpenpathSearchStandard(ToolsUnitTests):
55
+ """LangChain's own checks for a tool."""
56
+
57
+ @property
58
+ def tool_constructor(self):
59
+ return OpenpathSearch
60
+
61
+ @property
62
+ def tool_constructor_params(self):
63
+ return {"client": FakeClient()}
64
+
65
+ @property
66
+ def tool_invoke_params_example(self):
67
+ return {"query": "plumber near Austin, TX"}