hotdata-langchain 0.2.1__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.
- hotdata_langchain-0.2.1/.github/dependabot.yml +9 -0
- hotdata_langchain-0.2.1/.github/workflows/check-release.yml +26 -0
- hotdata_langchain-0.2.1/.github/workflows/ci.yml +33 -0
- hotdata_langchain-0.2.1/.github/workflows/dependabot-automerge.yml +17 -0
- hotdata_langchain-0.2.1/.github/workflows/publish.yml +69 -0
- hotdata_langchain-0.2.1/.github/workflows/release.yml +54 -0
- hotdata_langchain-0.2.1/.gitignore +23 -0
- hotdata_langchain-0.2.1/CHANGELOG.md +44 -0
- hotdata_langchain-0.2.1/PKG-INFO +102 -0
- hotdata_langchain-0.2.1/README.md +91 -0
- hotdata_langchain-0.2.1/RELEASING.md +43 -0
- hotdata_langchain-0.2.1/examples/langchain_basic.py +21 -0
- hotdata_langchain-0.2.1/examples/langchain_managed_db.py +38 -0
- hotdata_langchain-0.2.1/hotdata_langchain/__init__.py +38 -0
- hotdata_langchain-0.2.1/hotdata_langchain/databases.py +60 -0
- hotdata_langchain-0.2.1/hotdata_langchain/tools.py +103 -0
- hotdata_langchain-0.2.1/pyproject.toml +70 -0
- hotdata_langchain-0.2.1/scripts/check-release.py +68 -0
- hotdata_langchain-0.2.1/scripts/extract-changelog.py +36 -0
- hotdata_langchain-0.2.1/scripts/release.sh +187 -0
- hotdata_langchain-0.2.1/scripts/update_changelog.py +62 -0
- hotdata_langchain-0.2.1/tests/conftest.py +29 -0
- hotdata_langchain-0.2.1/tests/test_package.py +41 -0
- hotdata_langchain-0.2.1/tests/test_tools.py +113 -0
- hotdata_langchain-0.2.1/tests/test_update_changelog.py +48 -0
- hotdata_langchain-0.2.1/uv.lock +1776 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Check release metadata
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- 'pyproject.toml'
|
|
7
|
+
- 'CHANGELOG.md'
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
check:
|
|
14
|
+
name: Verify changelog matches version bump
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
|
|
25
|
+
- name: Check release metadata
|
|
26
|
+
run: python scripts/check-release.py
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main", "master"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
name: Test (Python 3.12)
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
21
|
+
|
|
22
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v6
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
run: uv python install 3.12
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: uv sync --locked
|
|
31
|
+
|
|
32
|
+
- name: Test
|
|
33
|
+
run: uv run pytest -v
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Dependabot auto-merge
|
|
2
|
+
|
|
3
|
+
on: pull_request
|
|
4
|
+
|
|
5
|
+
permissions:
|
|
6
|
+
contents: write
|
|
7
|
+
pull-requests: write
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
auto-merge:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
if: github.actor == 'dependabot[bot]'
|
|
13
|
+
steps:
|
|
14
|
+
- name: Enable auto-merge
|
|
15
|
+
run: gh pr merge --squash --auto "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}"
|
|
16
|
+
env:
|
|
17
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v[0-9]*'
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: pypi-publish-${{ github.ref_name }}
|
|
10
|
+
cancel-in-progress: false
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
name: Build distribution
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
23
|
+
with:
|
|
24
|
+
python-version: '3.12'
|
|
25
|
+
|
|
26
|
+
- name: Install build tooling
|
|
27
|
+
run: python -m pip install --upgrade build twine
|
|
28
|
+
|
|
29
|
+
- name: Verify tag matches pyproject version
|
|
30
|
+
run: |
|
|
31
|
+
if [[ ! "$GITHUB_REF_NAME" =~ ^v[0-9] ]]; then
|
|
32
|
+
echo "Release tag '$GITHUB_REF_NAME' must start with 'v' followed by a digit (e.g. v1.0.0)" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
36
|
+
pkg_version=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
|
|
37
|
+
if [ "$tag" != "$pkg_version" ]; then
|
|
38
|
+
echo "Release tag ($tag) does not match pyproject.toml version ($pkg_version)" >&2
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
- name: Build sdist and wheel
|
|
43
|
+
run: python -m build
|
|
44
|
+
|
|
45
|
+
- name: Check distribution metadata
|
|
46
|
+
run: python -m twine check --strict dist/*
|
|
47
|
+
|
|
48
|
+
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
|
|
53
|
+
publish:
|
|
54
|
+
name: Publish to PyPI
|
|
55
|
+
needs: build
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
environment:
|
|
58
|
+
name: pypi
|
|
59
|
+
url: https://pypi.org/p/hotdata-langchain
|
|
60
|
+
permissions:
|
|
61
|
+
id-token: write
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
|
64
|
+
with:
|
|
65
|
+
name: dist
|
|
66
|
+
path: dist/
|
|
67
|
+
|
|
68
|
+
- name: Publish via Trusted Publishing
|
|
69
|
+
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: GitHub Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v[0-9]*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
name: Create GitHub Release
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
19
|
+
with:
|
|
20
|
+
python-version: '3.12'
|
|
21
|
+
|
|
22
|
+
- name: Read package metadata
|
|
23
|
+
id: meta
|
|
24
|
+
run: |
|
|
25
|
+
pkg_name=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['name'])")
|
|
26
|
+
pkg_version="${GITHUB_REF_NAME#v}"
|
|
27
|
+
echo "name=${pkg_name}" >> "$GITHUB_OUTPUT"
|
|
28
|
+
echo "version=${pkg_version}" >> "$GITHUB_OUTPUT"
|
|
29
|
+
|
|
30
|
+
- name: Extract changelog notes
|
|
31
|
+
id: notes
|
|
32
|
+
run: |
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
version="${GITHUB_REF_NAME#v}"
|
|
35
|
+
if [[ -f CHANGELOG.md ]]; then
|
|
36
|
+
body="$(python scripts/extract-changelog.py "$version")"
|
|
37
|
+
else
|
|
38
|
+
body="Release ${version}."
|
|
39
|
+
fi
|
|
40
|
+
delimiter="EOF_${RANDOM}_${RANDOM}"
|
|
41
|
+
{
|
|
42
|
+
echo "body<<${delimiter}"
|
|
43
|
+
echo "$body"
|
|
44
|
+
echo "${delimiter}"
|
|
45
|
+
} >> "$GITHUB_OUTPUT"
|
|
46
|
+
|
|
47
|
+
- name: Create GitHub Release
|
|
48
|
+
uses: softprops/action-gh-release@1e812e8210a4a8a0b23075e5795f2a4e2b2a0b7 # v2.2.2
|
|
49
|
+
with:
|
|
50
|
+
tag_name: ${{ github.ref_name }}
|
|
51
|
+
name: ${{ steps.meta.outputs.name }} ${{ steps.meta.outputs.version }}
|
|
52
|
+
body: ${{ steps.notes.outputs.body }}
|
|
53
|
+
generate_release_notes: false
|
|
54
|
+
make_latest: true
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Virtual environments
|
|
8
|
+
.env
|
|
9
|
+
.venv
|
|
10
|
+
env/
|
|
11
|
+
venv/
|
|
12
|
+
|
|
13
|
+
# Testing
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov/
|
|
17
|
+
|
|
18
|
+
# Packaging
|
|
19
|
+
*.egg-info/
|
|
20
|
+
dist/
|
|
21
|
+
build/
|
|
22
|
+
|
|
23
|
+
.DS_Store
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [0.2.1] - 2026-06-22
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Pin `hotdata-runtime` to `>=0.3.0` (adds the typed-error API:
|
|
16
|
+
`HotdataError`/`HotdataTransientError`/`HotdataTerminalError`/`classify_sdk_error`).
|
|
17
|
+
No code adoption was required: this package has no SDK error-handling call sites — its
|
|
18
|
+
runtime calls are thin pass-throughs exposed as LangChain `StructuredTool`s, which let
|
|
19
|
+
exceptions propagate to the LangChain runtime by design.
|
|
20
|
+
|
|
21
|
+
## [0.2.0] - 2026-06-22
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Upgrade `hotdata` SDK pin to `>=0.4.1` and `hotdata-runtime` to `>=0.2.4`.
|
|
26
|
+
- Raise `langchain-core` floor to `>=1.0` (verified against the test suite).
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Ruff and mypy tooling configuration in `pyproject.toml`, plus `ruff` and `mypy`
|
|
31
|
+
dev dependencies. Applied `ruff check --fix` and `ruff format` cleanup across the
|
|
32
|
+
codebase.
|
|
33
|
+
|
|
34
|
+
## [0.1.1] - 2026-06-01
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Release 0.1.1
|
|
39
|
+
|
|
40
|
+
## [0.1.0] - 2026-05-19
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- Initial release with LangChain tools for Hotdata managed databases.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hotdata-langchain
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: LangChain tools for Hotdata runtime
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: hotdata-runtime>=0.3.0
|
|
8
|
+
Requires-Dist: hotdata>=0.4.1
|
|
9
|
+
Requires-Dist: langchain-core>=1.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# hotdata-langchain
|
|
13
|
+
|
|
14
|
+
Give your [LangChain](https://python.langchain.com/) agents access to [Hotdata](https://hotdata.dev) — run SQL against your workspace connections and work with managed databases.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install hotdata-langchain
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Authentication
|
|
23
|
+
|
|
24
|
+
Set `HOTDATA_API_KEY` in your environment. Optionally set `HOTDATA_WORKSPACE` to pin a specific workspace (the first available workspace is used if unset).
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
|
30
|
+
import hotdata_langchain as hl
|
|
31
|
+
|
|
32
|
+
client = hl.from_env()
|
|
33
|
+
tools = hl.make_hotdata_tools(client)
|
|
34
|
+
|
|
35
|
+
agent = create_tool_calling_agent(llm=your_llm, tools=tools, prompt=your_prompt)
|
|
36
|
+
executor = AgentExecutor(agent=agent, tools=tools)
|
|
37
|
+
result = executor.invoke({"input": "How many rows are in the orders table?"})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Tools
|
|
41
|
+
|
|
42
|
+
`make_hotdata_tools(client)` returns a list of LangChain `StructuredTool` objects ready to pass to any agent:
|
|
43
|
+
|
|
44
|
+
| Tool | What it does |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `hotdata_execute_sql` | Run a SQL query and return rows as JSON |
|
|
47
|
+
| `hotdata_list_managed_databases` | List available managed databases |
|
|
48
|
+
| `hotdata_create_managed_database` | Create a new managed database |
|
|
49
|
+
| `hotdata_load_managed_table` | Load a parquet file into a managed table |
|
|
50
|
+
|
|
51
|
+
## Calling tools directly
|
|
52
|
+
|
|
53
|
+
You can also invoke tools outside of an agent loop:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
tools = {t.name: t for t in hl.make_hotdata_tools(client)}
|
|
57
|
+
|
|
58
|
+
result = tools["hotdata_execute_sql"].invoke({"sql": "SELECT * FROM orders LIMIT 10"})
|
|
59
|
+
print(result) # JSON rows
|
|
60
|
+
|
|
61
|
+
tools["hotdata_create_managed_database"].invoke({
|
|
62
|
+
"name": "sales",
|
|
63
|
+
"schema_name": "public",
|
|
64
|
+
"tables": "orders,customers",
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
tools["hotdata_load_managed_table"].invoke({
|
|
68
|
+
"database": "sales",
|
|
69
|
+
"table": "orders",
|
|
70
|
+
"file": "/path/to/orders.parquet",
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Scoping queries to a managed database
|
|
75
|
+
|
|
76
|
+
Pass `database=` so all SQL the agent runs resolves against a specific managed database:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
tools = hl.make_hotdata_tools(client, database="sales")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Controlling result size
|
|
83
|
+
|
|
84
|
+
Limit how many rows are returned to the LLM. Useful for keeping responses within context limits (default: 100):
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
tools = hl.make_hotdata_tools(client, max_rows=50)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Run the examples
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv run python examples/langchain_basic.py
|
|
94
|
+
uv run python examples/langchain_managed_db.py
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
uv sync --locked
|
|
101
|
+
uv run pytest
|
|
102
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# hotdata-langchain
|
|
2
|
+
|
|
3
|
+
Give your [LangChain](https://python.langchain.com/) agents access to [Hotdata](https://hotdata.dev) — run SQL against your workspace connections and work with managed databases.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install hotdata-langchain
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Authentication
|
|
12
|
+
|
|
13
|
+
Set `HOTDATA_API_KEY` in your environment. Optionally set `HOTDATA_WORKSPACE` to pin a specific workspace (the first available workspace is used if unset).
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
|
19
|
+
import hotdata_langchain as hl
|
|
20
|
+
|
|
21
|
+
client = hl.from_env()
|
|
22
|
+
tools = hl.make_hotdata_tools(client)
|
|
23
|
+
|
|
24
|
+
agent = create_tool_calling_agent(llm=your_llm, tools=tools, prompt=your_prompt)
|
|
25
|
+
executor = AgentExecutor(agent=agent, tools=tools)
|
|
26
|
+
result = executor.invoke({"input": "How many rows are in the orders table?"})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Tools
|
|
30
|
+
|
|
31
|
+
`make_hotdata_tools(client)` returns a list of LangChain `StructuredTool` objects ready to pass to any agent:
|
|
32
|
+
|
|
33
|
+
| Tool | What it does |
|
|
34
|
+
|------|-------------|
|
|
35
|
+
| `hotdata_execute_sql` | Run a SQL query and return rows as JSON |
|
|
36
|
+
| `hotdata_list_managed_databases` | List available managed databases |
|
|
37
|
+
| `hotdata_create_managed_database` | Create a new managed database |
|
|
38
|
+
| `hotdata_load_managed_table` | Load a parquet file into a managed table |
|
|
39
|
+
|
|
40
|
+
## Calling tools directly
|
|
41
|
+
|
|
42
|
+
You can also invoke tools outside of an agent loop:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
tools = {t.name: t for t in hl.make_hotdata_tools(client)}
|
|
46
|
+
|
|
47
|
+
result = tools["hotdata_execute_sql"].invoke({"sql": "SELECT * FROM orders LIMIT 10"})
|
|
48
|
+
print(result) # JSON rows
|
|
49
|
+
|
|
50
|
+
tools["hotdata_create_managed_database"].invoke({
|
|
51
|
+
"name": "sales",
|
|
52
|
+
"schema_name": "public",
|
|
53
|
+
"tables": "orders,customers",
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
tools["hotdata_load_managed_table"].invoke({
|
|
57
|
+
"database": "sales",
|
|
58
|
+
"table": "orders",
|
|
59
|
+
"file": "/path/to/orders.parquet",
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Scoping queries to a managed database
|
|
64
|
+
|
|
65
|
+
Pass `database=` so all SQL the agent runs resolves against a specific managed database:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
tools = hl.make_hotdata_tools(client, database="sales")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Controlling result size
|
|
72
|
+
|
|
73
|
+
Limit how many rows are returned to the LLM. Useful for keeping responses within context limits (default: 100):
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
tools = hl.make_hotdata_tools(client, max_rows=50)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Run the examples
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv run python examples/langchain_basic.py
|
|
83
|
+
uv run python examples/langchain_managed_db.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Development
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv sync --locked
|
|
90
|
+
uv run pytest
|
|
91
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
Every release uses `./scripts/release.sh`. Do not bump versions, tag, or create GitHub Releases manually.
|
|
4
|
+
|
|
5
|
+
## One-time setup
|
|
6
|
+
|
|
7
|
+
- Install [GitHub CLI](https://cli.github.com/) (`gh`) and authenticate.
|
|
8
|
+
- Ensure PyPI [trusted publishing](https://docs.pypi.org/trusted-publishers/) is configured for this repo (`publish.yml` uses the `pypi` GitHub environment).
|
|
9
|
+
|
|
10
|
+
## Release steps
|
|
11
|
+
|
|
12
|
+
1. Add user-facing notes under `## [Unreleased]` in `CHANGELOG.md`.
|
|
13
|
+
2. Prepare the release PR:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
./scripts/release.sh prepare patch # or minor | major | 1.2.3
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
3. Merge the PR after CI passes (including the changelog check).
|
|
20
|
+
4. Publish from a clean default branch checkout:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git checkout main # or master for hotdata-marimo
|
|
24
|
+
git pull
|
|
25
|
+
./scripts/release.sh publish
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What happens automatically
|
|
29
|
+
|
|
30
|
+
Pushing a `vX.Y.Z` tag triggers two workflows:
|
|
31
|
+
|
|
32
|
+
| Workflow | Purpose |
|
|
33
|
+
|----------|---------|
|
|
34
|
+
| `publish.yml` | Build wheel/sdist and publish to PyPI |
|
|
35
|
+
| `release.yml` | Create the GitHub Release with notes from `CHANGELOG.md` |
|
|
36
|
+
|
|
37
|
+
## Enforcement
|
|
38
|
+
|
|
39
|
+
- **PR check** (`check-release.yml`): if `pyproject.toml` version changes, `CHANGELOG.md` must contain a matching `## [X.Y.Z]` section.
|
|
40
|
+
- **Tag check** (`publish.yml`): the tag (without `v`) must match `[project].version` in `pyproject.toml`.
|
|
41
|
+
- **Publish guard** (`release.sh publish`): refuses to tag if the changelog section is missing.
|
|
42
|
+
|
|
43
|
+
Together, these make it hard to ship a version without changelog notes or a GitHub Release.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Minimal LangChain tool usage with hotdata-langchain."""
|
|
2
|
+
|
|
3
|
+
import hotdata_langchain as hl
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main() -> None:
|
|
7
|
+
client = hl.from_env()
|
|
8
|
+
tools = hl.make_hotdata_tools(client)
|
|
9
|
+
by_name = {tool.name: tool for tool in tools}
|
|
10
|
+
|
|
11
|
+
sql_tool = by_name["hotdata_execute_sql"]
|
|
12
|
+
print(sql_tool.invoke({"sql": "SELECT 1 AS ok"}))
|
|
13
|
+
|
|
14
|
+
list_tool = by_name["hotdata_list_managed_databases"]
|
|
15
|
+
print(list_tool.invoke({}))
|
|
16
|
+
|
|
17
|
+
client.close()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Managed database tools for LangChain agents."""
|
|
2
|
+
|
|
3
|
+
import hotdata_langchain as hl
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main() -> None:
|
|
7
|
+
client = hl.from_env()
|
|
8
|
+
tools = hl.make_hotdata_tools(client)
|
|
9
|
+
by_name = {tool.name: tool for tool in tools}
|
|
10
|
+
|
|
11
|
+
create = by_name["hotdata_create_managed_database"]
|
|
12
|
+
print(
|
|
13
|
+
create.invoke(
|
|
14
|
+
{
|
|
15
|
+
"name": "demo_sales",
|
|
16
|
+
"schema_name": "public",
|
|
17
|
+
"tables": "orders\ncustomers",
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
load = by_name["hotdata_load_managed_table"]
|
|
23
|
+
print(
|
|
24
|
+
load.invoke(
|
|
25
|
+
{
|
|
26
|
+
"database": "demo_sales",
|
|
27
|
+
"table": "orders",
|
|
28
|
+
"file": "/path/to/orders.parquet",
|
|
29
|
+
"schema_name": "public",
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
client.close()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""LangChain tools for Hotdata runtime."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("hotdata-langchain")
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
__version__ = "0.0.0+unknown"
|
|
9
|
+
|
|
10
|
+
from hotdata_runtime import HotdataClient, QueryResult, from_env
|
|
11
|
+
|
|
12
|
+
from hotdata_langchain.databases import (
|
|
13
|
+
create_managed_database,
|
|
14
|
+
list_managed_databases_json,
|
|
15
|
+
load_managed_table,
|
|
16
|
+
load_result_summary,
|
|
17
|
+
managed_database_summary,
|
|
18
|
+
)
|
|
19
|
+
from hotdata_langchain.tools import (
|
|
20
|
+
execute_sql_json,
|
|
21
|
+
make_hotdata_tools,
|
|
22
|
+
result_rows_for_llm,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"HotdataClient",
|
|
27
|
+
"QueryResult",
|
|
28
|
+
"__version__",
|
|
29
|
+
"create_managed_database",
|
|
30
|
+
"execute_sql_json",
|
|
31
|
+
"from_env",
|
|
32
|
+
"list_managed_databases_json",
|
|
33
|
+
"load_managed_table",
|
|
34
|
+
"load_result_summary",
|
|
35
|
+
"make_hotdata_tools",
|
|
36
|
+
"managed_database_summary",
|
|
37
|
+
"result_rows_for_llm",
|
|
38
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Managed database helpers for LangChain agents."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from hotdata_runtime import (
|
|
9
|
+
DEFAULT_SCHEMA,
|
|
10
|
+
HotdataClient,
|
|
11
|
+
LoadManagedTableResult,
|
|
12
|
+
ManagedDatabase,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def list_managed_databases_json(client: HotdataClient) -> str:
|
|
17
|
+
rows = [
|
|
18
|
+
{
|
|
19
|
+
"description": db.description,
|
|
20
|
+
"id": db.id,
|
|
21
|
+
"sql_prefix": f"{db.id}.{{schema}}.{{table}}",
|
|
22
|
+
}
|
|
23
|
+
for db in client.list_managed_databases()
|
|
24
|
+
]
|
|
25
|
+
return json.dumps(rows, indent=2)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_managed_database(
|
|
29
|
+
client: HotdataClient,
|
|
30
|
+
*,
|
|
31
|
+
name: str,
|
|
32
|
+
schema: str = DEFAULT_SCHEMA,
|
|
33
|
+
tables: list[str] | None = None,
|
|
34
|
+
) -> ManagedDatabase:
|
|
35
|
+
return client.create_managed_database(description=name, schema=schema, tables=tables)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_managed_table(
|
|
39
|
+
client: HotdataClient,
|
|
40
|
+
*,
|
|
41
|
+
database: str,
|
|
42
|
+
table: str,
|
|
43
|
+
file: str,
|
|
44
|
+
schema: str = DEFAULT_SCHEMA,
|
|
45
|
+
) -> LoadManagedTableResult:
|
|
46
|
+
return client.load_managed_table(database, table, schema=schema, file=file)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def managed_database_summary(db: ManagedDatabase) -> dict[str, str]:
|
|
50
|
+
return {"id": db.id, "description": db.description or db.id}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def load_result_summary(result: LoadManagedTableResult) -> dict[str, Any]:
|
|
54
|
+
return {
|
|
55
|
+
"connection_id": result.connection_id,
|
|
56
|
+
"schema_name": result.schema_name,
|
|
57
|
+
"table_name": result.table_name,
|
|
58
|
+
"row_count": result.row_count,
|
|
59
|
+
"full_name": result.full_name,
|
|
60
|
+
}
|