Frameworks

Connect Twin to LangChain or AutoGen

Register the browser as one tool in a LangChain or AutoGen agent with a five-line function — pointed at dispatch, so repeat work replays instead of re-planning.

If your agent already runs on LangChain or AutoGen you do not need to rebuild it to give it a browser. There is no SDK to install: the API is plain HTTPS with a Bearer header, so the adapter is a function. This guide shows the minimal wiring for each, and why it should point at dispatch rather than run.

How does a browser fit into a tool-using agent?

Both frameworks drive a model that decides which tools to call. You register one tool that takes a goal, a URL and a success condition; when the agent decides it needs to act on the web it calls that tool, and the whole observe-plan-act loop happens server-side. The agent's own model chooses the goal; it does not drive the browser step by step.

Why point the adapter at dispatch?

Because it makes every call cache-first without changing anything in your framework. The first time the agent asks for something new, dispatch compiles and caches a skill; every similar call after that replays it for 2 credits with no planner call. Pointing at /run instead re-reasons every time, which is correct only when you specifically want an uncached run.

Wiring up LangChain

One decorated function. Note the body shape: `url`, `prompt` and `success` — the third is required, and it is what stops the agent from reporting a green run that did nothing.

LangChain toolpython
import os, requests
from langchain_core.tools import tool

@tool
def twin_browser(url: str, prompt: str, success_text: str) -> dict:
    """Act on a web page. Give the target URL, a natural-language goal, and
    the text that will be visible on the page when the goal has succeeded."""
    r = requests.post(
        "https://twin-browser.com/api/v1/dispatch",
        headers={"Authorization": f"Bearer {os.environ['TWIN_API_KEY']}"},
        json={
            "url": url,
            "prompt": prompt,
            "success": {"kind": "textVisible", "value": success_text},
        },
        timeout=180,
    )
    r.raise_for_status()
    return r.json()   # → { "mode": "cache-hit" | …, "success": …, "steps": … }

# agent = create_react_agent(llm, tools=[twin_browser])

Wiring up AutoGen

The same function, registered as a callable tool on the assistant. AutoGen invokes it when the conversation calls for a browser action.

AutoGen toolpython
import os, requests
from autogen import AssistantAgent, register_function

def twin_browser(url: str, prompt: str, success_text: str) -> dict:
    """Run a browser goal and return the structured result."""
    r = requests.post(
        "https://twin-browser.com/api/v1/dispatch",
        headers={"Authorization": f"Bearer {os.environ['TWIN_API_KEY']}"},
        json={
            "url": url,
            "prompt": prompt,
            "success": {"kind": "textVisible", "value": success_text},
        },
        timeout=180,
    )
    return r.json()

assistant = AssistantAgent("assistant", llm_config=llm_config)
register_function(
    twin_browser, caller=assistant, executor=user_proxy,
    description="Act on a web page from a URL, a goal and a success condition.",
)

Handling the paused case

A sign-in goal can come back with status "paused" and a sessionId rather than a result. Your tool should return that to the agent as-is rather than raising: the agent can then decide to ask the human for a code (POST /api/v1/runs/{id}/resume) or to hand over a connect link. Swallowing the pause is how a 2FA wall turns into a silent failure.

Timeouts

A synchronous browser run can take a minute or more. Set a generous client timeout, and for work that may exceed it, POST to /api/v1/jobs instead and give the tool a job id to poll — that is the asynchronous shape, and it is a different guide.

Keep going

Common questions

Is there a Twin package for LangChain?
No, and you do not need one. The API is plain HTTPS with a Bearer header, so the adapter is the function above — which also means it never goes stale against a wrapper version.
Do I need a different key for each framework?
No. The same per-tenant Bearer key works across REST, MCP and both frameworks; authorization, billing and audit are uniform across every entry point.
Will my framework see the savings automatically?
Yes, if the adapter posts to /api/v1/dispatch. Matching and replay happen server-side and are transparent to LangChain or AutoGen — the response just says mode: "cache-hit" instead of "cache-miss-compiled".

Delegate the work. Keep the decision.

Hand off a real task, set the guardrails, and let repeated work compile into a skill that replays deterministically at near-zero cost.