tools — BaseTool + 64 first-party tool integrations

BaseTool + 64 first-party tool integrations

Module langchain_classic/tools/ — BaseTool + 64 first-party tool integrations. Part of the State & Tools group.

🎯 Why This Module Exists

This module is documented from the tools_deep.md Phase 1B research artifact. See sections below for design decisions, algorithms, error philosophy, performance, and evolution clues.

186
Source files
64
Sub-directories
State & Tools
Category
Python
Language

BaseTool Pydantic schema Async-aware 64 tools

Architecture

flowchart LR
  subgraph Core["langchain_classic/tools/"]
    BASE["Base abstraction"]
    IMPLS["Concrete implementations"]
    BASE --> IMPLS
  end
  subgraph Up["Upstream callers"]
    U1["chains/"]
    U2["agents/"]
  end
  subgraph Down["Downstream"]
    D1["langchain_core"]
    D2["External providers"]
  end
  Up --> Core
  Core --> Down

Data Flow

sequenceDiagram
  participant Caller as Caller
  participant Tools as tools
  participant Core as langchain_core
  participant Ext as External
  Caller->>Tools: invoke(input)
  Note over Tools: Validate & route
  Tools->>Core: delegate primitive
  Core->>Ext: provider call
  Ext-->>Core: response
  Core-->>Tools: typed output
  Tools-->>Caller: result

Existence rationale

tools/ provides the **BaseTool ABC and 60+ integration sub-modules** that connect agents to the outside world: search engines, file systems, SQL DBs, REST APIs, browsers, email, calendars, code interpreters, and more. The existence of a separate tools/ module — rather than baking calls directly into agents — is what makes LangChain's "I gave my LLM a tool" model uniform: every tool exposes the same (name, description, args_schema, _run, _arun) shape, so the agent only needs to know how to format the call. Without tools/, agent flavours would each ship their own tool protocol, and integrators would need a separate adapter per agent type.

Design decisions visible in the code

DecisionChoice madePlausible alternativesInferred rationale
Tool definition shapeBaseTool ABC: name, description, args_schema (Pydantic), _run/_arunPlain functions with introspection; Protocol typingPydantic schema → JSON-schema → OpenAI function spec without re-implementation; description used in prompts
Sync + async dual surfaceBoth _run and _arun; default _arun runs _run in a threadAsync-onlyMany integrations are blocking (subprocess, requests); thread fallback prevents deadlocks
Decorator path@tool decorator infers schema from type hintsManual StructuredTool.from_functionLowers the bar for one-off tools; still produces a real BaseTool
ToolkitsBaseToolkit.get_tools() returns related toolsJust import functionsSome integrations (Gmail, JIRA, SQL) come as a family — toolkit is the discovery surface
Render functionsrender_text_description, render_text_description_and_args, convert_to_openai_function/toolInline string concat in agentsLets each agent flavour render the same tool list to its preferred format
Tool exception handlingTools may set `handle_tool_error: bool \str \Callable`Always raiseLets a tool decide whether its errors should kill the agent or be returned as observation
Lazy integration loadingtools/__init__.py uses __getattr__ to forward to langchain_community.toolsEager importsPrevents the heavy dependency tail (Selenium, boto3, GMail SDK) from being imported on import langchain_classic
Reserved root toolsA few tools live in the package root (requests.py, python.py, serpapi.py)Move them under tools/Historical: they predate the tools/ package and are kept at root for stable imports

Algorithm deep-dives

1. Tool argument validation

Trace.

  1. BaseTool.run(tool_input) accepts either a string (single-arg tools) or a dict (multi-arg).
  2. _to_args_and_kwargs(tool_input) decides which and validates against args_schema.
  3. On validation error: if handle_tool_error set → return formatted error string; else raise.
  4. _run(*args, **kwargs) is invoked with validated values.
  5. Result is returned (or wrapped in ToolMessage for chat agents).

Why Pydantic. Three benefits at once: (a) descriptive error messages an LLM can self-correct from, (b) automatic JSON-schema generation for OpenAI/Anthropic tool calling, (c) field descriptions become documentation in the agent prompt.

2. convert_to_openai_function / convert_to_openai_tool

Problem. OpenAI's Chat Completions API needs {"name": ..., "description": ..., "parameters": <jsonschema>}. We have a BaseTool. Bridge.

Trace.

  1. Read tool.name, tool.description, tool.args_schema.
  2. Pydantic v1: args_schema.schema() → JSON-schema dict.
  3. Strip title properties and definitions references that OpenAI rejects.
  4. Wrap in {"type": "function", "function": {...}} for the tools-API; leave bare for the legacy functions API.

Edge case. Tools without args_schema (single string input) get a synthesised {"type": "object", "properties": {"__arg1": {"type": "string"}}, "required": ["__arg1"]} — required because OpenAI's spec mandates an object schema.

3. render_text_description_and_args

Problem. ReAct/MRKL agents expect tool docs inline in the prompt, like "calculator: a calculator. Input: a math expression.". Generate this from a list of BaseTools.

Trace. Iterate tools → for each, format f"{tool.name}: {tool.description}", optionally followed by a JSON dump of args_schema. Returned string is fed into the prompt template at {tools} placeholder.

This is what makes a single tools=[…] argument work across flavours: each flavour calls a different render function but all consume the same BaseTool list.

Error philosophy

Tools are at the trust boundary — anything from a network search to executing Python code. The module's policy:

Performance characteristics

Evolution clues

Step-by-Step Walkthrough

Follow a typical operation through the tools/ module:

  1. Caller invokes the public surface — usually .invoke(input) on a Runnable, or a legacy __call__(inputs_dict) on a Chain subclass.
  2. Pydantic validation — every entry point is a BaseModel; constructor args, input dicts, and tool args_schema are validated before any expensive work runs. Fail-fast on type errors.
  3. CallbackManager.configure — a fresh manager is built per call, fans on_*_start events to all subscribers (LangSmith tracer, stdout handler, custom logging), and stamps a UUID4 run_id for tree reconstruction.
  4. tools-specific logic — the module's core method runs: prompt rendering, retrieval, tool execution, memory load, document split, etc. See the deep-dive sections above.
  5. Provider call — typically a single HTTP request to OpenAI/Anthropic/etc., or a local SDK call. Retries are handled by tenacity with exponential backoff and a configurable max-attempts knob.
  6. Streaming fan-out — if the provider streams tokens, each chunk fires on_llm_new_token through the callback manager so downstream observers (UI, log, tracer) see partial output.
  7. OutputParser pass — if a parser is attached, the raw provider output is parsed into a structured Python object; OutputParserException may trigger a retry chain (RetryWithErrorOutputParser).
  8. Memory write-back — for chains/agents that have a BaseMemory attached, save_context(inputs, outputs) persists the turn before returning to the caller.
  9. on_*_end fan-out — the callback manager closes the run span; tracers serialise the Run tree and ship it to LangSmith asynchronously.
  10. Result returned — caller receives a typed object (AIMessage, dict, BaseModel, list[Document], ...) matching the module's declared output schema.

What Could Go Wrong

Failure Mode Trigger Detection Recovery
Provider 5xx / rate-limit OpenAI/Anthropic/etc. transient error HTTP status from SDK tenacity retries with exponential backoff up to max_retries; then surface to caller
Output parser failure LLM returned malformed JSON / wrong schema OutputParserException in parser RetryWithErrorOutputParser re-prompts with the parser error included; or surface
Pydantic validation Caller passes wrong-shaped dict ValidationError at __init__ / invoke Surface immediately — no fallback is correct here
Callback handler exception User-supplied tracer raises Try/except around each fan-out Logged and swallowed by default; raise_error=True to opt in to propagation
Provider auth missing OPENAI_API_KEY unset SDK AuthenticationError Surface with a helpful message; no retry (would just fail again)

Performance Profile

Hot Paths

Resource Usage

Directory Map

Source tree for tools
tools/
├── __init__.py
├── ainetwork/
├── amadeus/
├── arxiv/
├── azure_cognitive_services/
├── base.py
├── bearly/
├── bing_search/
├── brave_search/
├── clickup/
├── convert_to_openai.py
├── dataforseo_api_search/
├── ddg_search/
├── e2b_data_analysis/
├── edenai/
├── eleven_labs/
├── file_management/
├── github/
├── gitlab/
├── gmail/
├── golden_query/
├── google_cloud/
├── google_finance/
├── google_jobs/
├── google_lens/
├── google_places/
├── google_scholar/
├── google_search/
├── google_serper/
├── google_trends/
├── graphql/
├── human/
├── ifttt.py
├── interaction/
├── jira/
├── json/
├── memorize/
├── merriam_webster/
├── metaphor_search/
├── multion/
├── nasa/
├── nuclia/
├── office365/
├── openapi/
├── openweathermap/
├── playwright/
├── plugin.py
├── powerbi/
├── pubmed/
├── python/
├── reddit_search/
├── render.py
├── requests/
├── retriever.py
├── scenexplain/
├── searchapi/
├── searx_search/
├── shell/
├── slack/
├── sleep/
    ... (more files truncated)