# LangChain Source: https://www.hotdata.dev/docs/langchain Site index: https://www.hotdata.dev/llms.txt LangChain tools for Hotdata — give your chains and agents SQL execution and instant database access. ## Install ```bash pip install hotdata-langchain ``` ## Authentication Set `HOTDATA_API_KEY` in your environment. Optionally set `HOTDATA_WORKSPACE` to pin a specific workspace. ## Quickstart ```python from hotdata_framework import from_env from hotdata_langchain import make_hotdata_tools from langchain.agents import create_agent client = from_env() tools = make_hotdata_tools(client, database_id="dbid...") agent = create_agent( model="openai:gpt-4o", tools=tools, system_prompt="You are a data analyst.", ) result = agent.invoke( {"messages": [{"role": "user", "content": "How many orders are in the database?"}]} ) print(result["messages"][-1].content) ``` Queries run against a database scope, so pass `database_id=` (an instant database id). `from_env().list_managed_databases()` shows what is available in the workspace, with the id of each. ## Tools `make_hotdata_tools` returns a list of `StructuredTool` objects ready to pass to any LangChain agent. By default it returns five tools — `hotdata_describe_tables` is registered unless you pass `describe_tables=False` — plus `hotdata_search_text` when you configure full-text search: | Tool | Description | |------|-------------| | `hotdata_execute_sql` | Run SQL and return JSON rows | | `hotdata_list_managed_databases` | List Hotdata instant databases, with the id of each | | `hotdata_create_managed_database` | Create a database and declare tables | | `hotdata_load_managed_table` | Load a local parquet file into a table | | `hotdata_describe_tables` | List tables, or one table's columns and types | | `hotdata_search_text` | Full-text search an indexed column, ranked by relevance (opt-in) | ```python tools = make_hotdata_tools( client, max_rows=50, # rows returned to the agent per query (default 100) database_id="dbid...", # scope queries to a specific database id (optional) ) ``` ## Run SQL directly ```python from hotdata_langchain import execute_sql_json, result_rows_for_llm from hotdata_langchain.databases import resolve_database_by_id # Queries are database-scoped, and these take a resolved database — not an id # string — under the keyword `database`. database = resolve_database_by_id(client, "dbid...") # Returns a JSON string — useful for custom tool wrappers json_str = execute_sql_json(client, "SELECT * FROM orders LIMIT 5", database=database) # Returns list[dict] from a QueryResult, trimmed to max_rows result = client.execute_sql("SELECT * FROM orders LIMIT 100", database=database) rows = result_rows_for_llm(result, max_rows=20) ``` Three things to carry over from that example. The keyword is `database` here, while `make_hotdata_tools` above takes `database_id` — [hotdata-langchain#81](https://github.com/hotdata-dev/hotdata-langchain/issues/81) tracks reconciling the two. `execute_sql_json` takes the resolved database only and rejects a bare id string, so `resolve_database_by_id` is a required step rather than a convenience; it accepts an already-resolved database unchanged, so it is safe to call either way. And while `client.execute_sql` does accept a string, that path falls back to matching a database's display label, which is not unique — passing the resolved database is the dependable form for both. ## Instant databases ```python from hotdata_langchain.databases import ( create_managed_database, list_managed_databases_json, load_managed_table, ) # Create a database and declare tables db = create_managed_database( client, name="sales", schema="public", tables=["orders", "customers"], ) # Load a local parquet file into a table loaded = load_managed_table( client, database_id=db.id, table="orders", file="orders.parquet", ) print(f"Loaded {loaded.row_count} rows → {loaded.full_name}") # What the workspace holds — one entry per database print(list_managed_databases_json(client)) ``` A listing entry carries the database's `id` and its `description`, which is where the label passed as `name` at creation currently comes back. Address a database by its `id` — the label is a display string and is not unique. The spelling of that label field is still settling, so read it off a live listing rather than assuming a name for it. ## See also - [hotdata-langchain on GitHub](https://github.com/hotdata-dev/hotdata-langchain) - [LangChain](https://python.langchain.com) - [Python SDK](/docs/python-sdk) — low-level `hotdata` HTTP API client