Skip to main content

LangChain integration for the Yutori API — Navigator browser control, browser automation, web research, and monitoring

Project description

langchain-yutori

LangChain integration for the Yutori API — Navigator browser control, browser automation, deep research, and recurring web monitors.

Installation

pip install langchain langchain-yutori

This package is implemented as a standalone LangChain integration package. It uses the official yutori Python SDK for Browsing, Research, and Scouts, and wraps Navigator as a LangChain chat model. Installing langchain-yutori also installs the yutori Python package, plus the yutori CLI.

Components

Class Type Description
ChatYutoriNavigator ChatModel Yutori Navigator browser-control model (OpenAI-compatible, defaults to n1.5)
YutoriBrowsingTool BaseTool Execute web browsing tasks on a remote browser
YutoriResearchTool BaseTool Perform deep and broad research using 100+ tools
YutoriScoutingTool BaseTool Create and manage recurring web monitors with Scouts

Authentication

Recommended:

yutori auth login

This opens your browser and saves your API key locally for the SDK and this package to use.

Or set your API key via environment variable:

export YUTORI_API_KEY="yt_..."

Or pass it directly to each class.

Get your API key at platform.yutori.com.

Usage

ChatYutoriNavigator

Navigator is Yutori's pixels-to-actions LLM for browser navigation. It accepts screenshots and returns browser actions (click, type, scroll, etc.). The current version is n1.5 (the default); older versions like n1 remain selectable via the model argument.

from langchain_yutori import ChatYutoriNavigator
from langchain_core.messages import HumanMessage
from yutori.navigator import aplaywright_screenshot_to_data_url

llm = ChatYutoriNavigator()  # defaults to n1.5-latest, uses YUTORI_API_KEY env var
image_url = await aplaywright_screenshot_to_data_url(page)

message = HumanMessage(content=[
    {"type": "text", "text": "What is the next action to complete the task: 'Add item to cart'?"},
    {"type": "image_url", "image_url": {"url": image_url}},
])
response = llm.invoke([message])
# Returns tool_calls with browser actions

To pin to a specific Navigator version:

llm = ChatYutoriNavigator(model="n1-latest")     # older Navigator n1
llm = ChatYutoriNavigator(model="n1.5-latest")   # current Navigator n1.5 (default)

With Playwright, use the SDK helper to capture the screenshot and re-encode it as a WebP data URL at the resolution Navigator was trained on (1280×800, q90). ChatYutoriNavigator accepts image URLs but doesn't capture or preprocess screenshots itself.

If you execute returned browser actions yourself, Navigator coordinates are normalized to a 1000x1000 space. Convert them back into viewport pixels with the SDK helper:

from yutori.navigator import denormalize_coordinates

coords = [500, 250]
x, y = denormalize_coordinates(coords, width=1280, height=800)
await page.mouse.click(x, y)

Tool sets and request options

Navigator's available action set is server-side and version-tagged. Select it (and optionally disable specific actions) via first-class constructor params; they are forwarded to the OpenAI client's extra_body:

llm = ChatYutoriNavigator(
    tool_set="browser_tools_expanded-20260403",   # adds extract_elements, find, set_element_value, execute_js
    disable_tools=["hold_key", "drag"],
)

Other Navigator-specific request fields (e.g. json_schema for structured output) can be passed through extra_body directly — they're merged with the first-class params:

llm = ChatYutoriNavigator(
    tool_set="browser_tools_core-20260403",
    extra_body={"json_schema": {"type": "object", "properties": {...}}},
)

Multi-turn loop and message-history shape

After Navigator returns tool_calls, you execute the action in your browser, capture a fresh screenshot, and feed the result back as a ToolMessage whose content is a multimodal list of [text, image_url]. Navigator requires the new screenshot inside the tool message — not in a separate HumanMessage:

from langchain_core.messages import AIMessage, HumanMessage
from langchain_yutori import navigator_tool_result, trim_navigator_history
from yutori.navigator import aplaywright_screenshot_to_data_url, denormalize_coordinates

history = [
    HumanMessage(content=[
        {"type": "text", "text": "Search for Yutori on Google."},
        {"type": "image_url", "image_url": {"url": await aplaywright_screenshot_to_data_url(page)}},
    ])
]

while True:
    response: AIMessage = llm.invoke(trim_navigator_history(history))
    history.append(response)
    if not response.tool_calls:
        break  # Navigator finished the task; response.content has the summary

    last_idx = len(response.tool_calls) - 1
    for i, call in enumerate(response.tool_calls):
        result_text = await execute_in_browser(page, call)  # your code; use denormalize_coordinates etc.
        history.append(navigator_tool_result(
            tool_call_id=call["id"],
            result_text=result_text,
            current_url=page.url,
            # only the last result in a batched turn carries the next screenshot
            screenshot_data_url=(
                await aplaywright_screenshot_to_data_url(page) if i == last_idx else None
            ),
        ))

Notes:

  • Don't add a system message. Navigator's docs recommend placing extra instructions in the first user message instead.

  • Include Current URL: ... in the tool result text — it improves grounding.

  • Don't drop messages — only old screenshots. For long trajectories, use trim_navigator_history to drop image_url blocks from older messages while keeping every message and its text intact:

    from langchain_yutori import trim_navigator_history
    
    response = llm.invoke(trim_navigator_history(history))   # default keep_recent=2
    

    The Yutori SDK exposes equivalent helpers (trim_images_to_fit, trimmed_messages_to_fit, create_trimmed / acreate_trimmed) that operate on dict-shaped messages and bypass LangChain — use those if you call the SDK directly, otherwise prefer the LangChain helper above for llm.invoke(...) flows.

For the full Navigator input requirements and action schema, see the Yutori docs: https://docs.yutori.com/llm-quickstart and https://docs.yutori.com/reference/navigator

YutoriBrowsingTool

Runs a browser automation agent on Yutori's cloud browser. The tool creates the task and polls until it completes (up to 20 minutes by default; tasks typically take 5–15 minutes).

from langchain_yutori import YutoriBrowsingTool

tool = YutoriBrowsingTool()

result = tool.run({
    "task": "Find the price of the MacBook Pro 14-inch M4",
    "start_url": "https://www.apple.com",
})
print(result)  # JSON string with task result

In a LangChain agent:

from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_yutori import YutoriBrowsingTool

tools = [YutoriBrowsingTool()]
llm = ChatOpenAI(model="gpt-4o")

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant with web browsing capabilities."),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
executor.invoke({"input": "What's the current price of AAPL on Yahoo Finance?"})

YutoriResearchTool

Performs deep and broad research using Yutori's research agent (100+ MCP tools). Creates the task and polls until complete.

from langchain_yutori import YutoriResearchTool

tool = YutoriResearchTool()

result = tool.run({
    "query": "What are the top 5 AI coding assistants in 2026 and how do their pricing models compare?",
    "user_location": "San Francisco, CA, US",
})
print(result)  # JSON string with research report

YutoriScoutingTool

Manages Yutori Scouts — recurring web monitors that run on a schedule and surface findings.

from langchain_yutori import YutoriScoutingTool

tool = YutoriScoutingTool()

# Create a scout (runs daily by default)
result = tool.run({
    "action": "create",
    "query": "Monitor Hacker News for posts about browser automation agents",
    "output_interval": 3600,  # hourly
})

# List all scouts
scouts = tool.run({"action": "list"})

# Get updates from a specific scout
updates = tool.run({
    "action": "get_updates",
    "scout_id": "abc123...",
    "limit": 10,
})

# Pause / resume / delete
tool.run({"action": "pause", "scout_id": "abc123..."})
tool.run({"action": "resume", "scout_id": "abc123..."})
tool.run({"action": "delete", "scout_id": "abc123..."})

Configuration

Browsing and Research tools accept poll_interval (seconds between status checks, default 60, minimum 60) and timeout (max wait seconds, default 1200):

tool = YutoriBrowsingTool(
    api_key="yt_...",
    poll_interval=10.0,
    timeout=300.0,
)

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langchain_yutori-0.2.0.tar.gz (135.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langchain_yutori-0.2.0-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

Details for the file langchain_yutori-0.2.0.tar.gz.

File metadata

  • Download URL: langchain_yutori-0.2.0.tar.gz
  • Upload date:
  • Size: 135.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for langchain_yutori-0.2.0.tar.gz
Algorithm Hash digest
SHA256 604e2075a48755465cfcbf45df26feb648a104afb56d82b3a2954f036bba9fd3
MD5 50a082e4ed4140903ca985ab801ae402
BLAKE2b-256 5ad5ec473f99ac2b67987b45037eedbebf848e8ca7f5d54cb6d289e80a6e14ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_yutori-0.2.0.tar.gz:

Publisher: publish-langchain-yutori.yml on yutori-ai/langchain-yutori

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file langchain_yutori-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for langchain_yutori-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f8e269456b8a5a08793fe23c48a03dfc97d381adcaefb431420d9c68a9519006
MD5 ede0a57eec0ded692d6cb740d9db40d9
BLAKE2b-256 cf20b632b95558eb9dfe8aaccf1b6717e8c10d509de6d8f221a5e3a5f454eb77

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_yutori-0.2.0-py3-none-any.whl:

Publisher: publish-langchain-yutori.yml on yutori-ai/langchain-yutori

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page