langchain-contextfetch 0.1.0__py3-none-any.whl
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.
- langchain_contextfetch/__init__.py +19 -0
- langchain_contextfetch/_utilities.py +55 -0
- langchain_contextfetch/py.typed +0 -0
- langchain_contextfetch/retrievers.py +122 -0
- langchain_contextfetch/tools.py +131 -0
- langchain_contextfetch-0.1.0.dist-info/METADATA +125 -0
- langchain_contextfetch-0.1.0.dist-info/RECORD +9 -0
- langchain_contextfetch-0.1.0.dist-info/WHEEL +4 -0
- langchain_contextfetch-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""LangChain integration for ContextFetch."""
|
|
2
|
+
|
|
3
|
+
from importlib import metadata
|
|
4
|
+
|
|
5
|
+
from langchain_contextfetch.retrievers import ContextFetchRetriever
|
|
6
|
+
from langchain_contextfetch.tools import ContextFetchSearch
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
__version__ = metadata.version(__package__ or "langchain_contextfetch")
|
|
10
|
+
except metadata.PackageNotFoundError:
|
|
11
|
+
# Case where package metadata is not available.
|
|
12
|
+
__version__ = ""
|
|
13
|
+
del metadata # optional, avoids polluting the results of dir(__package__)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"ContextFetchRetriever",
|
|
17
|
+
"ContextFetchSearch",
|
|
18
|
+
"__version__",
|
|
19
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Helpers shared by the retriever and the tool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from contextfetch import ContextFetch, Result, SearchResponse
|
|
8
|
+
from pydantic import SecretStr
|
|
9
|
+
|
|
10
|
+
Content = Literal["text", "pruned_context"]
|
|
11
|
+
"""Which result content to use: the whole page as Markdown, or only the chunks
|
|
12
|
+
relevant to the query."""
|
|
13
|
+
|
|
14
|
+
_RESULT_KEY = {"text": "text", "pruned_context": "prunedContext"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def search(
|
|
18
|
+
api_key: SecretStr | None,
|
|
19
|
+
query: str,
|
|
20
|
+
*,
|
|
21
|
+
num_results: int,
|
|
22
|
+
content: Content,
|
|
23
|
+
include_domains: list[str] | None = None,
|
|
24
|
+
exclude_domains: list[str] | None = None,
|
|
25
|
+
start_published_date: str | None = None,
|
|
26
|
+
) -> SearchResponse:
|
|
27
|
+
"""Run one search, asking the API only for the content that will be used."""
|
|
28
|
+
# ContextFetch() falls back to CONTEXTFETCH_API_KEY, then to the keyless tier.
|
|
29
|
+
client = ContextFetch(api_key.get_secret_value() if api_key else None)
|
|
30
|
+
return client.search(
|
|
31
|
+
query,
|
|
32
|
+
num_results=num_results,
|
|
33
|
+
include_domains=include_domains,
|
|
34
|
+
exclude_domains=exclude_domains,
|
|
35
|
+
start_published_date=start_published_date,
|
|
36
|
+
text=content == "text",
|
|
37
|
+
pruned_context=content == "pruned_context",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def page_content(result: Result, content: Content) -> str:
|
|
42
|
+
"""The text of a result for the chosen content type."""
|
|
43
|
+
return result.get(_RESULT_KEY[content]) or "" # type: ignore[return-value]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def metadata(result: Result) -> dict[str, Any]:
|
|
47
|
+
"""Result fields other than the page content."""
|
|
48
|
+
return {
|
|
49
|
+
"id": result.get("id"),
|
|
50
|
+
"url": result.get("url"),
|
|
51
|
+
"title": result.get("title"),
|
|
52
|
+
"score": result.get("score"),
|
|
53
|
+
"published_date": result.get("publishedDate"),
|
|
54
|
+
"author": result.get("author"),
|
|
55
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""ContextFetch retrievers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
|
8
|
+
from langchain_core.documents import Document
|
|
9
|
+
from langchain_core.retrievers import BaseRetriever
|
|
10
|
+
from langchain_core.utils import secret_from_env
|
|
11
|
+
from pydantic import Field, SecretStr
|
|
12
|
+
|
|
13
|
+
from langchain_contextfetch._utilities import Content, metadata, page_content, search
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ContextFetchRetriever(BaseRetriever):
|
|
17
|
+
"""ContextFetch web search retriever.
|
|
18
|
+
|
|
19
|
+
Setup:
|
|
20
|
+
Install `langchain-contextfetch` and set the environment variable
|
|
21
|
+
`CONTEXTFETCH_API_KEY`. Without a key, requests use the keyless tier.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -U langchain-contextfetch
|
|
25
|
+
export CONTEXTFETCH_API_KEY="your-api-key"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Key init args:
|
|
29
|
+
k: int
|
|
30
|
+
Number of results to return (1 to 25). Default 5.
|
|
31
|
+
content: "text" | "pruned_context"
|
|
32
|
+
`"text"` uses the whole page as Markdown; `"pruned_context"` uses only
|
|
33
|
+
the chunks of the page relevant to the query. Default `"text"`.
|
|
34
|
+
include_domains: list[str] | None
|
|
35
|
+
Only return results from these domains.
|
|
36
|
+
exclude_domains: list[str] | None
|
|
37
|
+
Never return results from these domains.
|
|
38
|
+
start_published_date: str | None
|
|
39
|
+
Only return pages published on or after this ISO date.
|
|
40
|
+
api_key: SecretStr | None
|
|
41
|
+
ContextFetch API key. Defaults to `CONTEXTFETCH_API_KEY`.
|
|
42
|
+
|
|
43
|
+
Instantiate:
|
|
44
|
+
```python
|
|
45
|
+
from langchain_contextfetch import ContextFetchRetriever
|
|
46
|
+
|
|
47
|
+
retriever = ContextFetchRetriever(k=3)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Usage:
|
|
51
|
+
```python
|
|
52
|
+
docs = retriever.invoke("pgvector HNSW index parameters")
|
|
53
|
+
docs[0].metadata["url"]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use within a chain:
|
|
57
|
+
```python
|
|
58
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
59
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
60
|
+
from langchain_core.runnables import RunnablePassthrough
|
|
61
|
+
from langchain_anthropic import ChatAnthropic
|
|
62
|
+
|
|
63
|
+
prompt = ChatPromptTemplate.from_template(
|
|
64
|
+
\"\"\"Answer the question based only on the context provided.
|
|
65
|
+
|
|
66
|
+
Context: {context}
|
|
67
|
+
|
|
68
|
+
Question: {question}\"\"\"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
model = ChatAnthropic(model="claude-sonnet-4-6")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def format_docs(docs):
|
|
75
|
+
return "\\n\\n".join(doc.page_content for doc in docs)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
chain = (
|
|
79
|
+
{"context": retriever | format_docs, "question": RunnablePassthrough()}
|
|
80
|
+
| prompt
|
|
81
|
+
| model
|
|
82
|
+
| StrOutputParser()
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
chain.invoke("How do I tune HNSW in pgvector?")
|
|
86
|
+
```
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
k: int = Field(default=5, ge=1, le=25)
|
|
90
|
+
"""Number of results to return (1 to 25)."""
|
|
91
|
+
content: Content = "text"
|
|
92
|
+
"""`"text"` (whole page as Markdown, the default) or `"pruned_context"`
|
|
93
|
+
(query-relevant chunks of each page)."""
|
|
94
|
+
include_domains: list[str] | None = None
|
|
95
|
+
"""Only return results from these domains."""
|
|
96
|
+
exclude_domains: list[str] | None = None
|
|
97
|
+
"""Never return results from these domains."""
|
|
98
|
+
start_published_date: str | None = None
|
|
99
|
+
"""Only return pages published on or after this ISO date (YYYY-MM-DD)."""
|
|
100
|
+
api_key: SecretStr | None = Field(
|
|
101
|
+
default_factory=secret_from_env("CONTEXTFETCH_API_KEY", default=None)
|
|
102
|
+
)
|
|
103
|
+
"""ContextFetch API key. Defaults to the `CONTEXTFETCH_API_KEY` env var."""
|
|
104
|
+
|
|
105
|
+
# Async: BaseRetriever runs this method in a thread executor, since the
|
|
106
|
+
# contextfetch SDK is synchronous (stdlib urllib).
|
|
107
|
+
def _get_relevant_documents(
|
|
108
|
+
self, query: str, *, run_manager: CallbackManagerForRetrieverRun, **kwargs: Any
|
|
109
|
+
) -> list[Document]:
|
|
110
|
+
response = search(
|
|
111
|
+
self.api_key,
|
|
112
|
+
query,
|
|
113
|
+
num_results=kwargs.get("k", self.k),
|
|
114
|
+
content=self.content,
|
|
115
|
+
include_domains=self.include_domains,
|
|
116
|
+
exclude_domains=self.exclude_domains,
|
|
117
|
+
start_published_date=self.start_published_date,
|
|
118
|
+
)
|
|
119
|
+
return [
|
|
120
|
+
Document(page_content=page_content(r, self.content), metadata=metadata(r))
|
|
121
|
+
for r in response.get("results", [])
|
|
122
|
+
]
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""ContextFetch tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from contextfetch import ContextFetchError, SearchResponse
|
|
8
|
+
from langchain_core.callbacks import CallbackManagerForToolRun
|
|
9
|
+
from langchain_core.tools import BaseTool, ToolException
|
|
10
|
+
from langchain_core.utils import secret_from_env
|
|
11
|
+
from pydantic import BaseModel, Field, SecretStr
|
|
12
|
+
|
|
13
|
+
from langchain_contextfetch._utilities import Content, page_content, search
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ContextFetchSearchInput(BaseModel):
|
|
17
|
+
"""Input schema for the ContextFetch search tool.
|
|
18
|
+
|
|
19
|
+
The Field descriptions are sent to the model when performing tool calling.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
query: str = Field(description="The web search query.")
|
|
23
|
+
include_domains: list[str] | None = Field(
|
|
24
|
+
default=None, description="Only return results from these domains."
|
|
25
|
+
)
|
|
26
|
+
exclude_domains: list[str] | None = Field(
|
|
27
|
+
default=None, description="Never return results from these domains."
|
|
28
|
+
)
|
|
29
|
+
start_published_date: str | None = Field(
|
|
30
|
+
default=None,
|
|
31
|
+
description="Only return pages published on or after this date (YYYY-MM-DD).",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ContextFetchSearch(BaseTool): # type: ignore[override]
|
|
36
|
+
"""ContextFetch web search tool.
|
|
37
|
+
|
|
38
|
+
Setup:
|
|
39
|
+
Install `langchain-contextfetch` and set the environment variable
|
|
40
|
+
`CONTEXTFETCH_API_KEY`. Without a key, requests use the keyless tier.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install -U langchain-contextfetch
|
|
44
|
+
export CONTEXTFETCH_API_KEY="your-api-key"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Instantiation:
|
|
48
|
+
```python
|
|
49
|
+
from langchain_contextfetch import ContextFetchSearch
|
|
50
|
+
|
|
51
|
+
tool = ContextFetchSearch(num_results=3)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Invocation with args:
|
|
55
|
+
```python
|
|
56
|
+
tool.invoke({"query": "pgvector HNSW index parameters"})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Invocation with ToolCall:
|
|
60
|
+
```python
|
|
61
|
+
tool.invoke(
|
|
62
|
+
{
|
|
63
|
+
"args": {"query": "pgvector HNSW index parameters"},
|
|
64
|
+
"id": "1",
|
|
65
|
+
"name": tool.name,
|
|
66
|
+
"type": "tool_call",
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Returns a `ToolMessage` whose `content` lists each result's title, URL,
|
|
72
|
+
published date and page content, and whose `artifact` is the raw API
|
|
73
|
+
response (a dict with `results`).
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
name: str = "contextfetch_search"
|
|
77
|
+
"""The name that is passed to the model when performing tool calling."""
|
|
78
|
+
description: str = (
|
|
79
|
+
"Search the web. Returns the top results, each with its title, URL, "
|
|
80
|
+
"published date and page content as Markdown."
|
|
81
|
+
)
|
|
82
|
+
"""The description that is passed to the model when performing tool calling."""
|
|
83
|
+
args_schema: type[BaseModel] = ContextFetchSearchInput
|
|
84
|
+
"""The schema that is passed to the model when performing tool calling."""
|
|
85
|
+
response_format: Literal["content", "content_and_artifact"] = "content_and_artifact"
|
|
86
|
+
|
|
87
|
+
num_results: int = Field(default=5, ge=1, le=25)
|
|
88
|
+
"""Number of results per search (1 to 25)."""
|
|
89
|
+
content: Content = "pruned_context"
|
|
90
|
+
"""`"pruned_context"` (query-relevant chunks of each page, the default) or
|
|
91
|
+
`"text"` (whole page as Markdown)."""
|
|
92
|
+
api_key: SecretStr | None = Field(
|
|
93
|
+
default_factory=secret_from_env("CONTEXTFETCH_API_KEY", default=None)
|
|
94
|
+
)
|
|
95
|
+
"""ContextFetch API key. Defaults to the `CONTEXTFETCH_API_KEY` env var."""
|
|
96
|
+
|
|
97
|
+
# Async: BaseTool runs this method in a thread executor, since the
|
|
98
|
+
# contextfetch SDK is synchronous (stdlib urllib).
|
|
99
|
+
def _run(
|
|
100
|
+
self,
|
|
101
|
+
query: str,
|
|
102
|
+
include_domains: list[str] | None = None,
|
|
103
|
+
exclude_domains: list[str] | None = None,
|
|
104
|
+
start_published_date: str | None = None,
|
|
105
|
+
*,
|
|
106
|
+
run_manager: CallbackManagerForToolRun | None = None,
|
|
107
|
+
) -> tuple[str, SearchResponse]:
|
|
108
|
+
try:
|
|
109
|
+
response = search(
|
|
110
|
+
self.api_key,
|
|
111
|
+
query,
|
|
112
|
+
num_results=self.num_results,
|
|
113
|
+
content=self.content,
|
|
114
|
+
include_domains=include_domains,
|
|
115
|
+
exclude_domains=exclude_domains,
|
|
116
|
+
start_published_date=start_published_date,
|
|
117
|
+
)
|
|
118
|
+
except ContextFetchError as e:
|
|
119
|
+
# Raised as ToolException so handle_tool_error can pass it to the model.
|
|
120
|
+
raise ToolException(str(e)) from e
|
|
121
|
+
results = response.get("results", [])
|
|
122
|
+
if not results:
|
|
123
|
+
return "No results found.", response
|
|
124
|
+
content = "\n\n---\n\n".join(
|
|
125
|
+
f"Title: {r.get('title') or ''}\n"
|
|
126
|
+
f"URL: {r.get('url', '')}\n"
|
|
127
|
+
f"Published: {r.get('publishedDate') or 'unknown'}\n\n"
|
|
128
|
+
f"{page_content(r, self.content)}"
|
|
129
|
+
for r in results
|
|
130
|
+
)
|
|
131
|
+
return content, response
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: langchain-contextfetch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An integration package connecting ContextFetch and LangChain
|
|
5
|
+
Project-URL: Homepage, https://contextfetch.dev
|
|
6
|
+
Project-URL: Documentation, https://contextfetch.dev/docs/reference
|
|
7
|
+
Project-URL: Source Code, https://github.com/lachy3433/contextfetch/tree/main/integrations/langchain
|
|
8
|
+
Project-URL: Repository, https://github.com/lachy3433/contextfetch
|
|
9
|
+
Author: ContextFetch
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: <4.0.0,>=3.10.0
|
|
20
|
+
Requires-Dist: contextfetch<1.0.0,>=0.1.0
|
|
21
|
+
Requires-Dist: langchain-core<2.0.0,>=1.0.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# langchain-contextfetch
|
|
25
|
+
|
|
26
|
+
This package contains the LangChain integration with [ContextFetch](https://contextfetch.dev), a web search API that returns pages as Markdown.
|
|
27
|
+
|
|
28
|
+
- `ContextFetchRetriever`: a retriever that returns search results as `Document`s.
|
|
29
|
+
- `ContextFetchSearch`: a search tool for agents and tool-calling models.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install -U langchain-contextfetch
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Requires Python 3.10 or later.
|
|
38
|
+
|
|
39
|
+
## Credentials
|
|
40
|
+
|
|
41
|
+
Set your API key in the `CONTEXTFETCH_API_KEY` environment variable, or pass `api_key=` to either class:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export CONTEXTFETCH_API_KEY="your-api-key"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Without a key, requests use ContextFetch's keyless tier, which has a small daily allowance per IP address. See the [API reference](https://contextfetch.dev/docs/reference) for keys and credits.
|
|
48
|
+
|
|
49
|
+
## Retriever
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from langchain_contextfetch import ContextFetchRetriever
|
|
53
|
+
|
|
54
|
+
retriever = ContextFetchRetriever(k=3)
|
|
55
|
+
docs = retriever.invoke("pgvector HNSW index parameters")
|
|
56
|
+
|
|
57
|
+
for doc in docs:
|
|
58
|
+
print(doc.metadata["url"], doc.metadata["title"])
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Each `Document` has:
|
|
62
|
+
|
|
63
|
+
- `page_content`: the page as Markdown (`content="text"`, the default), or only the chunks of the page relevant to the query (`content="pruned_context"`).
|
|
64
|
+
- `metadata`: `id`, `url`, `title`, `score`, `published_date` and `author`. Fields the API does not return are `None`.
|
|
65
|
+
|
|
66
|
+
Options:
|
|
67
|
+
|
|
68
|
+
| Parameter | Default | Description |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `k` | `5` | Number of results (1 to 25). Can also be passed per call: `retriever.invoke(query, k=10)`. |
|
|
71
|
+
| `content` | `"text"` | `"text"` or `"pruned_context"`. |
|
|
72
|
+
| `include_domains` | `None` | Only return results from these domains. |
|
|
73
|
+
| `exclude_domains` | `None` | Never return results from these domains. |
|
|
74
|
+
| `start_published_date` | `None` | Only return pages published on or after this date (`YYYY-MM-DD`). |
|
|
75
|
+
| `api_key` | `CONTEXTFETCH_API_KEY` | API key. |
|
|
76
|
+
|
|
77
|
+
## Search tool
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from langchain_contextfetch import ContextFetchSearch
|
|
81
|
+
|
|
82
|
+
tool = ContextFetchSearch(num_results=3)
|
|
83
|
+
tool.invoke({"query": "pgvector HNSW index parameters"})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The model can set `query`, `include_domains`, `exclude_domains` and `start_published_date`. `num_results` (default 5, maximum 25), `content` and `api_key` are set when the tool is created. The tool's default `content` is `"pruned_context"`, so each result contributes only its query-relevant chunks to the model's context; use `content="text"` for whole pages.
|
|
87
|
+
|
|
88
|
+
The tool uses `response_format="content_and_artifact"`. Called with plain args as above, it returns the content string: each result's title, URL, published date and content. Called with a `ToolCall`, it returns a `ToolMessage` with that `content` and the raw API response as its `artifact`.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
msg = tool.invoke(
|
|
92
|
+
{
|
|
93
|
+
"args": {"query": "pgvector HNSW index parameters"},
|
|
94
|
+
"id": "1",
|
|
95
|
+
"name": tool.name,
|
|
96
|
+
"type": "tool_call",
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
msg.artifact["results"][0]["url"]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
API errors (for example HTTP 402 when credits run out) are raised as `ToolException`. To pass the error message to the model instead, create the tool with `handle_tool_error=True`.
|
|
103
|
+
|
|
104
|
+
### Use in an agent
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from langchain.agents import create_agent
|
|
108
|
+
|
|
109
|
+
agent = create_agent(model="claude-sonnet-4-6", tools=[tool])
|
|
110
|
+
agent.invoke({"messages": [{"role": "user", "content": "What changed in Python 3.14?"}]})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Async
|
|
114
|
+
|
|
115
|
+
Both classes support `ainvoke`. The underlying `contextfetch` SDK is synchronous, so async calls run in a thread executor (LangChain's default for tools and retrievers).
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
make test # unit tests, network disabled
|
|
121
|
+
make integration_test # live API tests; skipped unless CONTEXTFETCH_API_KEY is set
|
|
122
|
+
make lint
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The unit tests include LangChain's standard `ToolsUnitTests`. The integration tests run the standard `ToolsIntegrationTests` and `RetrieversIntegrationTests` suites, which make 10 searches in total.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
langchain_contextfetch/__init__.py,sha256=PuSElyC8k3iCofeOMHmqV3jdsqA2qHN_oR3W-RfRQCw,564
|
|
2
|
+
langchain_contextfetch/_utilities.py,sha256=EgInXZQjgLJ7P3hYwRHqfceII7UJ7n28LkYjrYBErJU,1798
|
|
3
|
+
langchain_contextfetch/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
langchain_contextfetch/retrievers.py,sha256=9_AV8_CUPwIxYkuL2eFD6Y5-yKyKV60IkcevVRt6eYg,4249
|
|
5
|
+
langchain_contextfetch/tools.py,sha256=LIJVmDeF7XjtUhRqYtBEb0vPXp5Cep9YLYiHQqUdhfo,4731
|
|
6
|
+
langchain_contextfetch-0.1.0.dist-info/METADATA,sha256=85wdlSXxbRBjzxMlK6kXQMRTS3De9nWiAHZTSK150Cs,4933
|
|
7
|
+
langchain_contextfetch-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
8
|
+
langchain_contextfetch-0.1.0.dist-info/licenses/LICENSE,sha256=RqQqv3BiTnGfaC6tdJ9oSSRKa0fzw4e7wxJwAR9Bab4,1073
|
|
9
|
+
langchain_contextfetch-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContextFetch
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|