Skip to content

Commit dc51a4e

Browse files
authored
feat(langchain): upgrade deepagents fetch integration (#76)
* feat(langchain): upgrade deepagents fetch integration * docs(langchain): trim deepagents fetch readme
1 parent fc53385 commit dc51a4e

4 files changed

Lines changed: 66 additions & 12 deletions

File tree

examples/integrations/langchain/deepagents-browserbase/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ It intentionally does **not** route the agent through the Browserbase CLI. Deep
1111
## Architecture
1212

1313
- `browserbase_search`: fast discovery with Browserbase Search
14-
- `browserbase_fetch`: cheap page retrieval with Browserbase Fetch
14+
- `browserbase_fetch`: Browserbase Fetch / Fetch Extract for raw, markdown, or structured JSON retrieval
1515
- `browserbase_rendered_extract`: Stagehand-backed rendered extraction for JS-heavy pages
1616
- `browserbase_interactive_task`: a Stagehand `agent().execute(...)` workflow for clicks, typing, login, or form submission
1717
- `browser-specialist` subagent: isolates browser-heavy work from the main planner
@@ -87,11 +87,14 @@ This is the right place to put human approval in a Deep Agents + Browserbase des
8787
## Notes
8888

8989
- The interactive tool now uses `stagehand.agent().execute(...)` instead of a single `sessions.act(...)` call. That makes it better suited to genuine multi-step browser tasks.
90+
- Browserbase Fetch supports `raw`, `markdown`, and `json` output in the Python SDK starting with `browserbase` `1.11.0`, which is why this example now requires that version or newer.
9091
- Browserbase’s Stagehand quickstart documents that Model Gateway works with just `BROWSERBASE_API_KEY` for Stagehand browser workflows.
9192
- I did not hardcode a Browserbase model-gateway URL for the LangChain model client because I did not find an official doc page in the Browserbase docs that specifies a general-purpose OpenAI-compatible endpoint for LangChain. The sample therefore accepts `DEEPAGENT_BASE_URL` or `OPENAI_BASE_URL` explicitly.
9293

9394
## Suggested prompts
9495

9596
- `Research Browserbase Search, Fetch, and browser sessions. Give me a decision tree with citations.`
97+
- `Use browserbase_fetch with markdown output on https://docs.browserbase.com/platform/fetch/overview and summarize the fetch limits.`
98+
- `Use browserbase_fetch with JSON output to extract the page title and one-sentence summary from https://www.browserbase.com/.`
9699
- `Open docs.browserbase.com and extract the limits of the Fetch API from the rendered docs page.`
97100
- `Go to example.com and tell me whether any interactive action would be required to complete the task.`

examples/integrations/langchain/deepagents-browserbase/browser_tools.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,47 @@ def browserbase_search(query: str, num_results: int = 5) -> str:
111111

112112

113113
@tool
114-
def browserbase_fetch(url: str, use_proxy: bool = False, max_chars: int = 12000) -> str:
114+
def browserbase_fetch(
115+
url: str,
116+
format: str = "markdown",
117+
schema: str = "",
118+
use_proxy: bool = False,
119+
allow_redirects: bool = False,
120+
allow_insecure_ssl: bool = False,
121+
max_chars: int = 12000,
122+
) -> str:
115123
"""Fetch page content without a browser session. Best for static pages and quick reads."""
116124
bb = _browserbase_client()
117-
response = bb.fetch_api.create(url=url, proxies=use_proxy)
125+
normalized_format = format.strip().lower() or "markdown"
126+
if normalized_format not in {"raw", "markdown", "json"}:
127+
raise ValueError("format must be one of: raw, markdown, json")
128+
129+
parsed_schema: dict[str, Any] | None = None
130+
if schema.strip():
131+
try:
132+
loaded_schema = json.loads(schema)
133+
except json.JSONDecodeError as exc:
134+
raise ValueError("schema must be valid JSON") from exc
135+
if not isinstance(loaded_schema, dict):
136+
raise ValueError("schema must decode to a JSON object")
137+
parsed_schema = loaded_schema
138+
139+
if normalized_format == "json" and parsed_schema is None:
140+
raise ValueError("schema is required when format='json'")
141+
if normalized_format != "json" and parsed_schema is not None:
142+
raise ValueError("schema can only be used when format='json'")
143+
144+
request: dict[str, Any] = {
145+
"url": url,
146+
"format": normalized_format,
147+
"proxies": use_proxy,
148+
"allow_redirects": allow_redirects,
149+
"allow_insecure_ssl": allow_insecure_ssl,
150+
}
151+
if parsed_schema is not None:
152+
request["schema"] = parsed_schema
153+
154+
response = bb.fetch_api.create(**request)
118155
content = getattr(response, "content", "")
119156
content_type = (
120157
getattr(response, "content_type", None)
@@ -123,20 +160,33 @@ def browserbase_fetch(url: str, use_proxy: bool = False, max_chars: int = 12000)
123160
).lower()
124161

125162
title = ""
126-
text = str(content)[:max_chars]
127-
if "html" in content_type:
128-
title, text = _html_to_text(str(content), max_chars=max_chars)
163+
text = ""
164+
structured_content: Any = None
165+
166+
if normalized_format == "json":
167+
structured_content = _normalize(content)
168+
else:
169+
text = str(content)[:max_chars]
170+
if normalized_format == "raw" and "html" in content_type:
171+
title, text = _html_to_text(str(content), max_chars=max_chars)
129172

130173
return _json(
131174
{
132175
"url": url,
176+
"format": normalized_format,
177+
"schema": parsed_schema,
178+
"used_proxy": use_proxy,
179+
"allow_redirects": allow_redirects,
180+
"allow_insecure_ssl": allow_insecure_ssl,
133181
"status_code": getattr(response, "status_code", None)
134182
or getattr(response, "statusCode", None),
183+
"headers": getattr(response, "headers", None),
135184
"content_type": getattr(response, "content_type", None)
136185
or getattr(response, "contentType", None),
137186
"encoding": getattr(response, "encoding", None),
138187
"title": title,
139188
"text": text,
189+
"content": structured_content,
140190
}
141191
)
142192

examples/integrations/langchain/deepagents-browserbase/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
2525
Workflow rules:
2626
- Start with browserbase_search for discovery unless the user already gave you a precise URL.
27-
- Prefer browserbase_fetch for quick reads of static pages.
27+
- Prefer browserbase_fetch with markdown output for quick reads of static pages.
28+
- Use browserbase_fetch with JSON output plus a schema when you need structured extraction from a non-JS page.
2829
- Delegate JS-heavy, rendered, or multi-step browsing work to the browser-specialist subagent.
2930
- Use browserbase_rendered_extract for read-only browser work on rendered pages.
3031
- Use browserbase_interactive_task only when the task requires clicking, typing, login, or form submission.
@@ -209,7 +210,7 @@ def parse_args() -> argparse.Namespace:
209210
"query",
210211
nargs="?",
211212
default=(
212-
"Research the Browserbase Search API and explain when to use Search, Fetch, "
213+
"Research the Browserbase Search and Fetch APIs and explain when to use Search, Fetch, "
213214
"and a full browser session. Cite the URLs you used."
214215
),
215216
)
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
beautifulsoup4>=4.13.0
2-
browserbase>=1.8.0
3-
deepagents>=0.0.5
4-
langchain-openai>=0.3.0
2+
browserbase>=1.11.0
3+
deepagents>=0.6.3
4+
langchain-openai>=1.2.1
55
python-dotenv>=1.0.0
6-
stagehand>=3.19.5
6+
stagehand>=3.20.0

0 commit comments

Comments
 (0)