langchain-tabstack 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.
- langchain_tabstack-0.1.0/.github/workflows/lint.yml +35 -0
- langchain_tabstack-0.1.0/.github/workflows/publish.yml +81 -0
- langchain_tabstack-0.1.0/.github/workflows/test.yml +36 -0
- langchain_tabstack-0.1.0/.gitignore +14 -0
- langchain_tabstack-0.1.0/AGENTS.md +70 -0
- langchain_tabstack-0.1.0/PKG-INFO +284 -0
- langchain_tabstack-0.1.0/README.md +255 -0
- langchain_tabstack-0.1.0/langchain_tabstack/__init__.py +66 -0
- langchain_tabstack-0.1.0/langchain_tabstack/client.py +72 -0
- langchain_tabstack-0.1.0/langchain_tabstack/errors.py +35 -0
- langchain_tabstack-0.1.0/langchain_tabstack/py.typed +0 -0
- langchain_tabstack-0.1.0/langchain_tabstack/schemas.py +100 -0
- langchain_tabstack-0.1.0/langchain_tabstack/tools.py +386 -0
- langchain_tabstack-0.1.0/pyproject.toml +53 -0
- langchain_tabstack-0.1.0/tests/test_tools.py +234 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
# Reusable workflow. Called by publish.yml and runs directly on PRs / pushes.
|
|
4
|
+
on:
|
|
5
|
+
workflow_call:
|
|
6
|
+
pull_request:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Lint Check
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
cache: pip
|
|
25
|
+
|
|
26
|
+
- name: Install dev dependencies
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install -e ".[dev]"
|
|
30
|
+
|
|
31
|
+
- name: Ruff lint
|
|
32
|
+
run: ruff check .
|
|
33
|
+
|
|
34
|
+
- name: Ruff format check
|
|
35
|
+
run: ruff format --check .
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [created]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write # Required for trusted publishing to PyPI
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
# Lint must pass before publishing.
|
|
13
|
+
lint:
|
|
14
|
+
name: Lint Check
|
|
15
|
+
uses: ./.github/workflows/lint.yml
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
# Tests must pass on all supported Python versions before publishing.
|
|
20
|
+
test:
|
|
21
|
+
name: Test Check
|
|
22
|
+
uses: ./.github/workflows/test.yml
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
|
|
26
|
+
publish:
|
|
27
|
+
name: Build and Publish
|
|
28
|
+
needs: [lint, test]
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
permissions:
|
|
31
|
+
id-token: write # Required for trusted publishing to PyPI
|
|
32
|
+
contents: read
|
|
33
|
+
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- name: Set up Python
|
|
38
|
+
uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: "3.12"
|
|
41
|
+
|
|
42
|
+
- name: Set version from release tag
|
|
43
|
+
run: |
|
|
44
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
45
|
+
echo "Releasing version: $VERSION"
|
|
46
|
+
# Anchored to line start so `target-version`/`requires-python` are left alone.
|
|
47
|
+
sed -i 's/^version = ".*"/version = "'"$VERSION"'"/' pyproject.toml
|
|
48
|
+
sed -i 's/^__version__ = ".*"/__version__ = "'"$VERSION"'"/' langchain_tabstack/__init__.py
|
|
49
|
+
echo "pyproject.toml:"; grep '^version = ' pyproject.toml
|
|
50
|
+
echo "__init__.py:"; grep '^__version__ = ' langchain_tabstack/__init__.py
|
|
51
|
+
|
|
52
|
+
- name: Install build tooling
|
|
53
|
+
run: |
|
|
54
|
+
python -m pip install --upgrade pip
|
|
55
|
+
python -m pip install build twine
|
|
56
|
+
|
|
57
|
+
- name: Build package
|
|
58
|
+
run: python -m build
|
|
59
|
+
|
|
60
|
+
- name: Check package
|
|
61
|
+
run: python -m twine check dist/*
|
|
62
|
+
|
|
63
|
+
- name: Publish to TestPyPI
|
|
64
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
65
|
+
with:
|
|
66
|
+
repository-url: https://test.pypi.org/legacy/
|
|
67
|
+
attestations: false
|
|
68
|
+
|
|
69
|
+
- name: Verify TestPyPI installation
|
|
70
|
+
run: |
|
|
71
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
72
|
+
echo "Waiting for TestPyPI to process the upload..."
|
|
73
|
+
sleep 30
|
|
74
|
+
python -m pip install \
|
|
75
|
+
--index-url https://test.pypi.org/simple/ \
|
|
76
|
+
--extra-index-url https://pypi.org/simple/ \
|
|
77
|
+
"langchain-tabstack==$VERSION"
|
|
78
|
+
python -c "import langchain_tabstack; print(f'Imported langchain-tabstack {langchain_tabstack.__version__}')"
|
|
79
|
+
|
|
80
|
+
- name: Publish to PyPI
|
|
81
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
# Reusable workflow. Called by publish.yml and runs directly on PRs / pushes.
|
|
4
|
+
on:
|
|
5
|
+
workflow_call:
|
|
6
|
+
pull_request:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
name: Test Check
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
cache: pip
|
|
29
|
+
|
|
30
|
+
- name: Install package and test dependencies
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
pip install -e ".[test]"
|
|
34
|
+
|
|
35
|
+
- name: Test
|
|
36
|
+
run: pytest -q
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# AGENTS.md: langchain-tabstack
|
|
2
|
+
|
|
3
|
+
Guide for AI agents working on this package. Humans: see `README.md`.
|
|
4
|
+
|
|
5
|
+
## What this package is
|
|
6
|
+
|
|
7
|
+
Tabstack tools for LangChain (Python). It wraps the official `tabstack` SDK and exposes Tabstack's
|
|
8
|
+
extract / research / generate / automate capabilities as native LangChain tools. It mirrors the
|
|
9
|
+
TypeScript `@tabstack/langchain` and `@tabstack/ai` packages tool-for-tool: same names, descriptions,
|
|
10
|
+
and input fields.
|
|
11
|
+
|
|
12
|
+
## Why use it
|
|
13
|
+
|
|
14
|
+
Schema-enforced web extraction and multi-source research as drop-in LangChain tools, backed by the
|
|
15
|
+
SDK (no bespoke HTTP, no Playwright). Consistent with the TypeScript packages so a cross-language
|
|
16
|
+
stack behaves the same.
|
|
17
|
+
|
|
18
|
+
## How to use it (consumer)
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from langchain_tabstack import TABSTACK_TOOLS, create_tabstack_tools
|
|
22
|
+
|
|
23
|
+
# TABSTACK_TOOLS: list using the default client (reads TABSTACK_API_KEY lazily)
|
|
24
|
+
# create_tabstack_tools(api_key=...) or (client=...) / (async_client=...) for a custom client
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Every tool supports both `invoke` (sync) and `ainvoke` (native async via `AsyncTabstack`).
|
|
28
|
+
|
|
29
|
+
## Architecture (single source of truth, mirror of the TS `core`)
|
|
30
|
+
|
|
31
|
+
| File | Responsibility |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `client.py` | Sync + async client construction, lazy defaults (read `TABSTACK_API_KEY`). |
|
|
34
|
+
| `schemas.py` | Pydantic input models. Single source of truth for the input surface. |
|
|
35
|
+
| `tools.py` | Tool names/descriptions, sync + async bodies, the `create_tabstack_tools` factory. |
|
|
36
|
+
| `errors.py` | `TabstackToolError` + `normalize_error`. |
|
|
37
|
+
|
|
38
|
+
## Rules specific to this package
|
|
39
|
+
|
|
40
|
+
- **Python 3.9 is the floor.** Use `typing.Optional/Dict/List`, not PEP 604/585 (`X | None`,
|
|
41
|
+
`list[...]`). Pydantic evaluates these annotations at runtime and the newer syntax breaks on 3.9.
|
|
42
|
+
This is why ruff's `UP` rules are disabled in `pyproject.toml`.
|
|
43
|
+
- All network work goes through the SDK client. No bespoke HTTP.
|
|
44
|
+
- Tools return **JSON strings** (`json.dumps`), the LangChain-Python convention. `extract_page_content`
|
|
45
|
+
returns the markdown string directly. Keys are snake_case (the TS packages use camelCase objects).
|
|
46
|
+
- `automate_browser_task` runs non-interactively: it does not enable the human-in-the-loop
|
|
47
|
+
`agent.automate_input` flow.
|
|
48
|
+
- Keep names, descriptions, and fields in lockstep with the TypeScript packages.
|
|
49
|
+
|
|
50
|
+
## Adding a tool
|
|
51
|
+
|
|
52
|
+
1. `schemas.py`: pydantic input model (reuse the shared field-description constants).
|
|
53
|
+
2. `tools.py`: `TOOL_NAMES`, `TOOL_DESCRIPTIONS`, a sync body, an async body, and `_build(...)` in
|
|
54
|
+
`create_tabstack_tools`.
|
|
55
|
+
3. `__init__.py`: export anything new.
|
|
56
|
+
4. Add tests, and mirror the tool into the TypeScript packages.
|
|
57
|
+
|
|
58
|
+
## Build / test
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
python -m venv .venv
|
|
62
|
+
.venv/bin/pip install -e ".[test,dev]"
|
|
63
|
+
.venv/bin/ruff check . && .venv/bin/ruff format --check .
|
|
64
|
+
.venv/bin/pytest -q
|
|
65
|
+
.venv/bin/python -m build # wheel + sdist
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Tests mock the SDK client (sync via `MagicMock`, async via coroutine side-effects) and assert names,
|
|
69
|
+
descriptions, schema fields, and execution routing, so drift fails CI. CI runs the matrix
|
|
70
|
+
3.9 / 3.10 / 3.11 / 3.12.
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: langchain-tabstack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tabstack tools for LangChain. Schema-enforced web extraction and research, backed by the official Tabstack SDK.
|
|
5
|
+
Project-URL: Homepage, https://tabstack.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.tabstack.ai
|
|
7
|
+
Project-URL: Source, https://github.com/Mozilla-Ocho/tabstack-langchain-python
|
|
8
|
+
Author: Tabstack
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: agent,extraction,langchain,tabstack,tools,web-scraping
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Requires-Dist: langchain-core<2,>=0.3
|
|
21
|
+
Requires-Dist: pydantic>=2
|
|
22
|
+
Requires-Dist: tabstack>=2.6.1
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest-mock>=3; extra == 'test'
|
|
27
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# langchain-tabstack
|
|
31
|
+
|
|
32
|
+
Give your [LangChain](https://python.langchain.com) agents reliable web access. Schema-enforced
|
|
33
|
+
extraction, multi-source research, AI transformation, and browser automation, all as native
|
|
34
|
+
LangChain tools backed by the official [Tabstack SDK](https://pypi.org/project/tabstack/).
|
|
35
|
+
|
|
36
|
+
## Why Tabstack
|
|
37
|
+
|
|
38
|
+
LangChain's built-in loaders (`WebBaseLoader`, `PlaywrightURLLoader`) are fine for prototypes and
|
|
39
|
+
brittle in production: unpredictable parsing, Playwright binaries to install and maintain, and
|
|
40
|
+
behaviour that drifts across LangChain releases.
|
|
41
|
+
|
|
42
|
+
Tabstack replaces them with a hosted API:
|
|
43
|
+
|
|
44
|
+
- **Schema-enforced output.** You define the shape, you get that shape back. No prompt-engineering
|
|
45
|
+
the JSON out of raw text.
|
|
46
|
+
- **No browser to run.** JS-heavy pages render server-side. No Playwright, no binaries.
|
|
47
|
+
- **Version-independent.** It is an SDK call, not a loader coupled to your LangChain version.
|
|
48
|
+
- **Sync and async.** Every tool supports `invoke` and native `ainvoke`.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install langchain-tabstack
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Requires Python 3.9+. For the agent example below you also need a chat model, for example
|
|
57
|
+
`pip install langchain langchain-openai`. Set your API key (get one at
|
|
58
|
+
[console.tabstack.ai](https://console.tabstack.ai)):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
export TABSTACK_API_KEY="your-key-here"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from langchain.agents import create_agent
|
|
68
|
+
from langchain_tabstack import TABSTACK_TOOLS
|
|
69
|
+
|
|
70
|
+
agent = create_agent(
|
|
71
|
+
"openai:gpt-4o",
|
|
72
|
+
tools=TABSTACK_TOOLS,
|
|
73
|
+
system_prompt=(
|
|
74
|
+
"You are a research assistant with web intelligence tools. "
|
|
75
|
+
"Use extract_structured_data for specific fields from a URL, "
|
|
76
|
+
"extract_page_content for full page text, and research_question for multi-source research."
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
result = agent.invoke(
|
|
81
|
+
{"messages": [{"role": "user", "content": "What are Vercel's pricing plans?"}]}
|
|
82
|
+
)
|
|
83
|
+
print(result["messages"][-1].content)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`TABSTACK_TOOLS` reads `TABSTACK_API_KEY` from the environment and is ready to drop into any agent.
|
|
87
|
+
|
|
88
|
+
> Using LangChain 0.x? `create_agent` is the LangChain 1.x replacement for `AgentExecutor` +
|
|
89
|
+
> `create_tool_calling_agent`. The tools themselves work with either.
|
|
90
|
+
|
|
91
|
+
## The tools
|
|
92
|
+
|
|
93
|
+
| Tool | What it does |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| `extract_structured_data` | Pull specific fields from a URL into a JSON shape you define. |
|
|
96
|
+
| `extract_page_content` | Fetch a page as clean markdown. |
|
|
97
|
+
| `research_question` | Synthesised answer with cited sources across multiple pages. |
|
|
98
|
+
| `generate_structured_data` | Fetch a page, then AI-transform it into derived or reshaped JSON. |
|
|
99
|
+
| `automate_browser_task` | Run a multi-step, natural-language browser task. |
|
|
100
|
+
|
|
101
|
+
Every tool is exported individually too, so you can use one directly without an agent. Tools return
|
|
102
|
+
a JSON string (use `json.loads`); `extract_page_content` returns markdown text directly.
|
|
103
|
+
|
|
104
|
+
### extract_structured_data
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
import json
|
|
108
|
+
from langchain_tabstack import extract_structured_data_tool
|
|
109
|
+
|
|
110
|
+
raw = extract_structured_data_tool.invoke(
|
|
111
|
+
{
|
|
112
|
+
"url": "https://news.ycombinator.com",
|
|
113
|
+
# json_schema_json is a JSON-encoded JSON Schema describing the fields you want.
|
|
114
|
+
"json_schema_json": json.dumps(
|
|
115
|
+
{
|
|
116
|
+
"type": "object",
|
|
117
|
+
"properties": {
|
|
118
|
+
"stories": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"items": {
|
|
121
|
+
"type": "object",
|
|
122
|
+
"properties": {
|
|
123
|
+
"title": {"type": "string"},
|
|
124
|
+
"points": {"type": "number", "description": "Story points"},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
data = json.loads(raw)
|
|
134
|
+
print(data["stories"])
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### extract_page_content
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from langchain_tabstack import extract_page_content_tool
|
|
141
|
+
|
|
142
|
+
markdown = extract_page_content_tool.invoke({"url": "https://example.com/blog/article"})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### research_question
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
import json
|
|
149
|
+
from langchain_tabstack import research_question_tool
|
|
150
|
+
|
|
151
|
+
result = json.loads(
|
|
152
|
+
research_question_tool.invoke(
|
|
153
|
+
{"query": "What are the latest developments in quantum error correction?"}
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
print(result["answer"])
|
|
157
|
+
for source in result["sources"]: # [{"title": ..., "url": ...}, ...]
|
|
158
|
+
print(source["url"])
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### generate_structured_data
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
import json
|
|
165
|
+
from langchain_tabstack import generate_structured_data_tool
|
|
166
|
+
|
|
167
|
+
raw = generate_structured_data_tool.invoke(
|
|
168
|
+
{
|
|
169
|
+
"url": "https://news.ycombinator.com",
|
|
170
|
+
"instructions": "For each story, categorize it and write a one-sentence summary.",
|
|
171
|
+
"json_schema_json": json.dumps(
|
|
172
|
+
{
|
|
173
|
+
"type": "object",
|
|
174
|
+
"properties": {
|
|
175
|
+
"summaries": {
|
|
176
|
+
"type": "array",
|
|
177
|
+
"items": {
|
|
178
|
+
"type": "object",
|
|
179
|
+
"properties": {
|
|
180
|
+
"title": {"type": "string"},
|
|
181
|
+
"category": {"type": "string"},
|
|
182
|
+
"summary": {"type": "string"},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
),
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### automate_browser_task
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
import json
|
|
197
|
+
from langchain_tabstack import automate_browser_task_tool
|
|
198
|
+
|
|
199
|
+
result = json.loads(
|
|
200
|
+
automate_browser_task_tool.invoke(
|
|
201
|
+
{
|
|
202
|
+
"task": "Find the top 3 trending repositories and their star counts",
|
|
203
|
+
"url": "https://github.com/trending",
|
|
204
|
+
"guardrails": "browse and extract only, do not submit forms",
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
# {"answer", "success", "iterations", "actions", "duration_ms", "extracted", "pages_visited"}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Optional inputs
|
|
212
|
+
|
|
213
|
+
Pass these alongside the required inputs for finer control. They are sent only when provided, so
|
|
214
|
+
omitting them keeps Tabstack's defaults.
|
|
215
|
+
|
|
216
|
+
- `extract_structured_data`, `extract_page_content`, `generate_structured_data`:
|
|
217
|
+
- `effort`: `"min"` | `"standard"` | `"max"`. Use `"max"` for JS-heavy pages (full server-side
|
|
218
|
+
browser rendering, the `PlaywrightURLLoader` replacement).
|
|
219
|
+
- `nocache`: `True` to bypass the cache.
|
|
220
|
+
- `country`: ISO 3166-1 alpha-2 code (for example `"US"`) for geotargeted fetches.
|
|
221
|
+
- `research_question`: `mode` (`"fast"` | `"balanced"`), `nocache`.
|
|
222
|
+
- `automate_browser_task`: `data` (context for form filling), `country`, `max_iterations`,
|
|
223
|
+
`max_validation_attempts`.
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
# Render a JS-heavy SPA, fresh, from the UK:
|
|
227
|
+
extract_page_content_tool.invoke(
|
|
228
|
+
{"url": url, "effort": "max", "nocache": True, "country": "GB"}
|
|
229
|
+
)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Async
|
|
233
|
+
|
|
234
|
+
Every tool supports `ainvoke` natively, backed by `AsyncTabstack`, so it runs in your event loop
|
|
235
|
+
without a thread-pool bounce:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
markdown = await extract_page_content_tool.ainvoke({"url": "https://example.com"})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Configuration
|
|
242
|
+
|
|
243
|
+
For a custom API key or base URL, or to reuse one client, build the tools explicitly:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
from langchain_tabstack import create_tabstack_tools
|
|
247
|
+
|
|
248
|
+
tools = create_tabstack_tools(api_key="...")
|
|
249
|
+
# or pass clients you already have:
|
|
250
|
+
# create_tabstack_tools(client=..., async_client=...)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Error handling
|
|
254
|
+
|
|
255
|
+
Tool calls raise `TabstackToolError` with a normalised message and, for API failures, an HTTP
|
|
256
|
+
`status`:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from langchain_tabstack import TabstackToolError, extract_page_content_tool
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
extract_page_content_tool.invoke({"url": "https://example.com"})
|
|
263
|
+
except TabstackToolError as err:
|
|
264
|
+
print(f"Tabstack failed ({err.status or 'no status'}): {err}")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Good to know
|
|
268
|
+
|
|
269
|
+
- **`automate_browser_task` runs non-interactively.** It does not pause for human-in-the-loop form
|
|
270
|
+
input, so it never blocks. It returns the final answer plus the data it extracted and the pages it
|
|
271
|
+
visited.
|
|
272
|
+
- The client is created lazily, so importing the package never requires an API key to be set.
|
|
273
|
+
- The tool names, descriptions, and inputs match the
|
|
274
|
+
[`@tabstack/langchain`](https://www.npmjs.com/package/@tabstack/langchain) (LangChain.js) and
|
|
275
|
+
[`@tabstack/ai`](https://www.npmjs.com/package/@tabstack/ai) (Vercel AI SDK) packages, so
|
|
276
|
+
behaviour is consistent across languages and frameworks. Those return native objects with
|
|
277
|
+
camelCase keys; this package returns JSON strings with snake_case keys, the LangChain-Python
|
|
278
|
+
convention. The data is the same.
|
|
279
|
+
|
|
280
|
+
## Links
|
|
281
|
+
|
|
282
|
+
- [Tabstack docs](https://docs.tabstack.ai)
|
|
283
|
+
- [Tabstack SDK (`tabstack`)](https://pypi.org/project/tabstack/)
|
|
284
|
+
- [Get an API key](https://console.tabstack.ai)
|