Skip to content

About

Fast browser agent on langchain-typesafe

Resources

Stars

44 stars

Watchers

1 watching

Forks

Repository files navigation

ts-browser-agent

A fast browser agent built on langchain-typesafe and the LangChain SDK. No third-party browser-agent package. The loop is LangChain's create_agent; TypeSafe is the model, and the browser actions are the tools.

Here, the agent plays the Wikipedia Game, going from LangChain to Microphone:

A recorded run of examples/wiki_game.py

Quickstart: run it in LangSmith Studio

  1. Install:

    uv sync
    uv run playwright install chromium
  2. Add a .env based on .env.example and fill in your values.

  3. Start the dev server:

    uv run langgraph dev

    Studio loads two graphs. browser is a deep agent that plans and delegates each page to the browse_fast tool. browser_loop is the browser agent itself, for watching the classifier's decisions one tool call at a time; it holds one browser, so run one goal at a time on it.

  4. Type a goal. The deep agent plans, hands each page to the browse_fast tool, and reports back. Each call opens a visible Chromium.

    Go to https://en.wikipedia.org/wiki/Main_Page and open the article about the Rosetta Stone. Tell me its first sentence.

    Find the cheapest direct flights between Montreal and Cancun for a Saturday-to-Saturday trip between December 2026 and February 2027.

Why langchain-typesafe

TypeSafeClassifier answers categorical questions about a JSON payload in one request. It returns probabilities over answers you offered, not generated text. A browser step fits: which operation, and which element. Trimmed from decision.py:

from langchain_typesafe import Choice, TypeSafeClassifier

classifier = TypeSafeClassifier(
    questions={
        "operation": Choice(
            instructions="Advance the goal from the current page using one operation.",
            criteria={"CLICK": "Click an element.", "TYPE_TEXT": "Type into a field.", "DONE": "..."},
        ),
        "click_target": Choice(
            instructions="Choose the best target if the next operation is CLICK.",
            criteria={"14": {"label": "Where to?"}, "21": {"label": "Cancún, Mexico"}},
        ),
    }
)
response = classifier.invoke({"goal": goal, "page": page, "elements": elements})
response.choices["operation"].choice            # "CLICK"
response.choices["click_target"].probabilities  # {"21": 0.91, "14": 0.09}

TypeSafeBrowserModel wraps that call as a chat model: each turn it reads the page from the last tool result and answers with one tool call, so create_agent runs it as it would any model.

Docs: provider guide · API reference · TypeSafe docs · speculative fan-out pattern · PyPI

Use it as a utility

Both Studio graphs are importable pieces.

Wrap browse_fast in your own deep agent

make_browse_fast_tool() returns a LangChain tool for create_agent or create_deep_agent.

from deepagents import create_deep_agent
from ts_browser_agent import make_browse_fast_tool

browse_fast = make_browse_fast_tool()  # headless=True, allow_private=False by default
agent = create_deep_agent(model="openai:gpt-5.5", tools=[browse_fast])
await agent.ainvoke({"messages": [("user", "Find the pricing page and summarize the tiers.")]})

A call returns a status line plus the final page's visible text, so the caller can read a price or a title from it. headless, allow_private, and text_model are fixed when the tool is built; they are not tool arguments. Each call builds its own browser agent, so parallel calls do not share a browser.

url comes from a model, so the open tool checks it with ensure_navigable before any browser launches.

See examples/deep_agent.py.

Build the browser agent

build_browser_agent() returns the compiled create_agent graph. It is async-only: drive it with ainvoke or astream.

import asyncio
from ts_browser_agent import build_browser_agent

agent = build_browser_agent()
goal = "Open the article about the Rosetta Stone.\n\nStart at https://en.wikipedia.org/wiki/Main_Page"
result = asyncio.run(agent.ainvoke({"messages": [("user", goal)]}))
print(result["messages"][-1].content)  # "DONE", "BLOCKED", or "STALLED: ..."

Name the start URL at the end of the goal. The classifier weighs the goal's opening heavily, and a goal that opens with the start page reads as anchored to it once the agent has moved on. DONE is the classifier's judgment, not a guarantee; verify before acting on it. Options: max_steps, headless, allow_private (permits a local dev server), and snapshot_filter, a hook applied to every observation before the model sees it — examples/wiki_game.py uses it to enforce the game's rules.

Examples

Every example opens a visible browser so you can watch.

Example Path What it exercises
flight_search.py agent A long click/select chain on real dynamic UI (Google Flights). Search only; it never books
github_issue.py agent Structurally different UI at each step (tab, filter, list, issue)
wiki_hop.py agent The same decision (first link in body text) made correctly many times in a row
wiki_game.py agent Reach one article from another (--start, --end). The game's rules live in a snapshot_filter: no revisits, article links only, no search
deep_agent.py deep agent The minimal shape: one browse_fast call, one page-scoped goal
uv run --env-file .env python examples/wiki_game.py --start "Jimmy Page" --end Microphone

Credits

The design follows jev-ultrafast by Browser Use: one TypeSafe request per step, a small model for typed text only, no LLM reasoning per click. The instruction text in decision.py is adapted from it under the MIT license. The implementation is independent — Playwright rather than Browser Harness, langchain-typesafe rather than a custom client, and LangChain's create_agent as the loop.

About

Fast browser agent on langchain-typesafe

Resources

Stars

44 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages