plasmate-browser-use 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ /target
2
+ *.swp
3
+ *.swo
4
+ *~
5
+ .DS_Store
6
+ report.md
7
+ .claude/settings.local.json
8
+ smoke/node_modules/
9
+ sdk/node/node_modules/
10
+ sdk/node/dist/
11
+ sdk/python/*.egg-info/
12
+ sdk/python/dist/
13
+ sdk/python/build/
14
+ __pycache__/
15
+ *.pyc
16
+
17
+ # MCP registry publisher tokens
18
+ .mcpregistry_*
19
+ .vercel
20
+ packages/*/node_modules/
21
+ packages/*/.venv/
22
+ packages/*/dist/
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.4
2
+ Name: plasmate-browser-use
3
+ Version: 0.3.0
4
+ Summary: Plasmate SOM integration for Browser Use - 10x fewer tokens for AI web agents
5
+ Project-URL: Homepage, https://plasmate.app
6
+ Project-URL: Documentation, https://plasmate.app/docs/integration-browser-use
7
+ Project-URL: Repository, https://github.com/plasmate-labs/plasmate
8
+ License-Expression: Apache-2.0
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: som-parser>=0.3.0
11
+ Provides-Extra: browser-use
12
+ Requires-Dist: browser-use>=0.12.0; extra == 'browser-use'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # plasmate-browser-use
16
+
17
+ SOM-based content extraction for [Browser Use](https://github.com/browser-use/browser-use). Drop-in alternative to Browser Use's default DOM serializer that uses Plasmate's Semantic Object Model (SOM) to reduce token costs by 10x or more.
18
+
19
+ Instead of sending the full DOM tree to your LLM, Plasmate compresses web pages into a compact semantic representation. Same information, 90% fewer tokens, lower costs, faster responses.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install plasmate-browser-use
25
+ ```
26
+
27
+ ## Prerequisites
28
+
29
+ You need the `plasmate` binary installed:
30
+
31
+ ```bash
32
+ # Via cargo
33
+ cargo install plasmate
34
+
35
+ # Or via install script
36
+ curl -fsSL https://plasmate.app/install.sh | sh
37
+ ```
38
+
39
+ Verify it works:
40
+
41
+ ```bash
42
+ plasmate --version
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ### Basic extraction
48
+
49
+ ```python
50
+ from plasmate_browser_use import PlasmateExtractor
51
+
52
+ extractor = PlasmateExtractor()
53
+
54
+ # Get raw SOM data as a dict
55
+ som = extractor.extract("https://news.ycombinator.com")
56
+ print(f"Elements: {som['meta']['element_count']}")
57
+ print(f"Compression: {som['meta']['html_bytes'] / som['meta']['som_bytes']:.1f}x")
58
+ ```
59
+
60
+ ### Get page context for an LLM
61
+
62
+ The `get_page_context()` method returns a formatted string optimized for LLM consumption, with interactive elements, links, content, and compression stats:
63
+
64
+ ```python
65
+ context = extractor.get_page_context("https://example.com")
66
+ print(context)
67
+ ```
68
+
69
+ Output:
70
+
71
+ ```
72
+ # Example Domain
73
+ URL: https://example.com
74
+ Language: en
75
+
76
+ ## Interactive Elements (1)
77
+ [e1] link "More information..." (click)
78
+
79
+ ## Content
80
+ This domain is for use in illustrative examples in documents...
81
+
82
+ ---
83
+ Compression: 15.2x (1256 HTML bytes -> 83 SOM bytes)
84
+ Elements: 5 (1 interactive)
85
+ ```
86
+
87
+ ### Markdown extraction
88
+
89
+ ```python
90
+ md = extractor.extract_markdown("https://example.com")
91
+ print(md)
92
+ ```
93
+
94
+ ### Async support
95
+
96
+ All methods have async variants:
97
+
98
+ ```python
99
+ import asyncio
100
+
101
+ async def main():
102
+ extractor = PlasmateExtractor()
103
+ context = await extractor.get_page_context_async("https://example.com")
104
+ som = await extractor.extract_async("https://example.com")
105
+ md = await extractor.extract_markdown_async("https://example.com")
106
+
107
+ asyncio.run(main())
108
+ ```
109
+
110
+ ### Using with a Browser Use agent
111
+
112
+ ```python
113
+ from browser_use import Agent
114
+ from plasmate_browser_use import PlasmateExtractor
115
+
116
+ extractor = PlasmateExtractor()
117
+
118
+ # Get compact page context instead of full DOM
119
+ context = extractor.get_page_context("https://example.com/products")
120
+
121
+ # Feed to your Browser Use agent with 10x fewer tokens
122
+ agent = Agent(task="Find the cheapest product", page_context=context)
123
+ result = await agent.run()
124
+ ```
125
+
126
+ ### Token savings comparison
127
+
128
+ ```python
129
+ from plasmate_browser_use import PlasmateExtractor, token_count_comparison
130
+
131
+ extractor = PlasmateExtractor()
132
+ som = extractor.extract("https://news.ycombinator.com")
133
+ stats = token_count_comparison(som)
134
+
135
+ print(f"HTML tokens: ~{stats['html_tokens_est']:,}")
136
+ print(f"SOM tokens: ~{stats['som_tokens_est']:,}")
137
+ print(f"Savings: {stats['token_savings_pct']}%")
138
+ print(f"Ratio: {stats['token_ratio']}x fewer tokens")
139
+ ```
140
+
141
+ ## Typical token savings
142
+
143
+ | Site | HTML tokens | SOM tokens | Reduction |
144
+ |------|------------|------------|-----------|
145
+ | Hacker News | ~22,000 | ~1,200 | 18x |
146
+ | Wikipedia article | ~85,000 | ~8,500 | 10x |
147
+ | Amazon product page | ~120,000 | ~6,000 | 20x |
148
+ | Google search results | ~45,000 | ~3,500 | 13x |
149
+
150
+ Numbers vary by page. The more complex the page (ads, trackers, layout noise), the bigger the savings.
151
+
152
+ ## How it works
153
+
154
+ 1. Plasmate fetches the page and parses the HTML
155
+ 2. The DOM is compiled into a Semantic Object Model (SOM) that preserves meaning while stripping layout noise
156
+ 3. The SOM is serialized into a compact format with tagged interactive elements
157
+ 4. Your LLM agent sees the same page information in 10x fewer tokens
158
+
159
+ ## Links
160
+
161
+ - [Plasmate](https://plasmate.app) -- the SOM engine
162
+ - [SOM Spec](https://plasmate.app/docs/som-spec) -- Semantic Object Model specification
163
+ - [Browser Use](https://github.com/browser-use/browser-use) -- AI agent browser framework
164
+ - [Token cost analysis](https://plasmate.app/docs/cost-analysis) -- detailed benchmarks
165
+
166
+ ## License
167
+
168
+ Apache-2.0
@@ -0,0 +1,154 @@
1
+ # plasmate-browser-use
2
+
3
+ SOM-based content extraction for [Browser Use](https://github.com/browser-use/browser-use). Drop-in alternative to Browser Use's default DOM serializer that uses Plasmate's Semantic Object Model (SOM) to reduce token costs by 10x or more.
4
+
5
+ Instead of sending the full DOM tree to your LLM, Plasmate compresses web pages into a compact semantic representation. Same information, 90% fewer tokens, lower costs, faster responses.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install plasmate-browser-use
11
+ ```
12
+
13
+ ## Prerequisites
14
+
15
+ You need the `plasmate` binary installed:
16
+
17
+ ```bash
18
+ # Via cargo
19
+ cargo install plasmate
20
+
21
+ # Or via install script
22
+ curl -fsSL https://plasmate.app/install.sh | sh
23
+ ```
24
+
25
+ Verify it works:
26
+
27
+ ```bash
28
+ plasmate --version
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ### Basic extraction
34
+
35
+ ```python
36
+ from plasmate_browser_use import PlasmateExtractor
37
+
38
+ extractor = PlasmateExtractor()
39
+
40
+ # Get raw SOM data as a dict
41
+ som = extractor.extract("https://news.ycombinator.com")
42
+ print(f"Elements: {som['meta']['element_count']}")
43
+ print(f"Compression: {som['meta']['html_bytes'] / som['meta']['som_bytes']:.1f}x")
44
+ ```
45
+
46
+ ### Get page context for an LLM
47
+
48
+ The `get_page_context()` method returns a formatted string optimized for LLM consumption, with interactive elements, links, content, and compression stats:
49
+
50
+ ```python
51
+ context = extractor.get_page_context("https://example.com")
52
+ print(context)
53
+ ```
54
+
55
+ Output:
56
+
57
+ ```
58
+ # Example Domain
59
+ URL: https://example.com
60
+ Language: en
61
+
62
+ ## Interactive Elements (1)
63
+ [e1] link "More information..." (click)
64
+
65
+ ## Content
66
+ This domain is for use in illustrative examples in documents...
67
+
68
+ ---
69
+ Compression: 15.2x (1256 HTML bytes -> 83 SOM bytes)
70
+ Elements: 5 (1 interactive)
71
+ ```
72
+
73
+ ### Markdown extraction
74
+
75
+ ```python
76
+ md = extractor.extract_markdown("https://example.com")
77
+ print(md)
78
+ ```
79
+
80
+ ### Async support
81
+
82
+ All methods have async variants:
83
+
84
+ ```python
85
+ import asyncio
86
+
87
+ async def main():
88
+ extractor = PlasmateExtractor()
89
+ context = await extractor.get_page_context_async("https://example.com")
90
+ som = await extractor.extract_async("https://example.com")
91
+ md = await extractor.extract_markdown_async("https://example.com")
92
+
93
+ asyncio.run(main())
94
+ ```
95
+
96
+ ### Using with a Browser Use agent
97
+
98
+ ```python
99
+ from browser_use import Agent
100
+ from plasmate_browser_use import PlasmateExtractor
101
+
102
+ extractor = PlasmateExtractor()
103
+
104
+ # Get compact page context instead of full DOM
105
+ context = extractor.get_page_context("https://example.com/products")
106
+
107
+ # Feed to your Browser Use agent with 10x fewer tokens
108
+ agent = Agent(task="Find the cheapest product", page_context=context)
109
+ result = await agent.run()
110
+ ```
111
+
112
+ ### Token savings comparison
113
+
114
+ ```python
115
+ from plasmate_browser_use import PlasmateExtractor, token_count_comparison
116
+
117
+ extractor = PlasmateExtractor()
118
+ som = extractor.extract("https://news.ycombinator.com")
119
+ stats = token_count_comparison(som)
120
+
121
+ print(f"HTML tokens: ~{stats['html_tokens_est']:,}")
122
+ print(f"SOM tokens: ~{stats['som_tokens_est']:,}")
123
+ print(f"Savings: {stats['token_savings_pct']}%")
124
+ print(f"Ratio: {stats['token_ratio']}x fewer tokens")
125
+ ```
126
+
127
+ ## Typical token savings
128
+
129
+ | Site | HTML tokens | SOM tokens | Reduction |
130
+ |------|------------|------------|-----------|
131
+ | Hacker News | ~22,000 | ~1,200 | 18x |
132
+ | Wikipedia article | ~85,000 | ~8,500 | 10x |
133
+ | Amazon product page | ~120,000 | ~6,000 | 20x |
134
+ | Google search results | ~45,000 | ~3,500 | 13x |
135
+
136
+ Numbers vary by page. The more complex the page (ads, trackers, layout noise), the bigger the savings.
137
+
138
+ ## How it works
139
+
140
+ 1. Plasmate fetches the page and parses the HTML
141
+ 2. The DOM is compiled into a Semantic Object Model (SOM) that preserves meaning while stripping layout noise
142
+ 3. The SOM is serialized into a compact format with tagged interactive elements
143
+ 4. Your LLM agent sees the same page information in 10x fewer tokens
144
+
145
+ ## Links
146
+
147
+ - [Plasmate](https://plasmate.app) -- the SOM engine
148
+ - [SOM Spec](https://plasmate.app/docs/som-spec) -- Semantic Object Model specification
149
+ - [Browser Use](https://github.com/browser-use/browser-use) -- AI agent browser framework
150
+ - [Token cost analysis](https://plasmate.app/docs/cost-analysis) -- detailed benchmarks
151
+
152
+ ## License
153
+
154
+ Apache-2.0
@@ -0,0 +1,24 @@
1
+ """Example: Using Plasmate SOM with Browser Use for cheaper AI browsing."""
2
+ import asyncio
3
+ from plasmate_browser_use import PlasmateExtractor
4
+
5
+
6
+ async def main():
7
+ extractor = PlasmateExtractor()
8
+
9
+ # Get SOM-formatted page context (10x fewer tokens than raw HTML)
10
+ context = extractor.get_page_context("https://news.ycombinator.com")
11
+ print(context)
12
+
13
+ # Or get structured SOM data
14
+ som = extractor.extract("https://news.ycombinator.com")
15
+ print(f"Compression: {som['meta']['html_bytes'] / som['meta']['som_bytes']:.1f}x")
16
+ print(f"Elements: {som['meta']['element_count']}")
17
+
18
+ # Or just markdown
19
+ md = extractor.extract_markdown("https://news.ycombinator.com")
20
+ print(md[:500])
21
+
22
+
23
+ if __name__ == "__main__":
24
+ asyncio.run(main())
@@ -0,0 +1,24 @@
1
+ """
2
+ Plasmate SOM integration for Browser Use.
3
+
4
+ Provides SOM-based content extraction that can replace or complement
5
+ Browser Use's default DOM serialization, reducing token costs by 90%+.
6
+
7
+ Example::
8
+
9
+ from plasmate_browser_use import PlasmateExtractor
10
+
11
+ extractor = PlasmateExtractor()
12
+ context = extractor.get_page_context("https://example.com")
13
+ print(context)
14
+ """
15
+
16
+ from .extractor import PlasmateExtractor
17
+ from .utils import token_count_comparison, estimate_tokens
18
+
19
+ __all__ = [
20
+ "PlasmateExtractor",
21
+ "token_count_comparison",
22
+ "estimate_tokens",
23
+ ]
24
+ __version__ = "0.3.0"
@@ -0,0 +1,145 @@
1
+ """Plasmate SOM extractor for Browser Use.
2
+
3
+ Provides SOM-based content extraction that can replace or complement
4
+ Browser Use's default DOM serialization, reducing token costs by 90%+.
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import subprocess
10
+ from typing import Optional
11
+
12
+ from som_parser import parse_som, get_links, get_interactive_elements, get_text, to_markdown
13
+
14
+
15
+ class PlasmateExtractor:
16
+ """Extract web page content using Plasmate's SOM format.
17
+
18
+ Use this alongside Browser Use to get structured, token-efficient
19
+ page representations instead of raw DOM serialization.
20
+ """
21
+
22
+ def __init__(self, plasmate_bin: str = "plasmate"):
23
+ self.plasmate_bin = plasmate_bin
24
+ self._verify_binary()
25
+
26
+ def _verify_binary(self):
27
+ """Check that plasmate binary is available."""
28
+ try:
29
+ result = subprocess.run(
30
+ [self.plasmate_bin, "--version"],
31
+ capture_output=True, text=True, timeout=5
32
+ )
33
+ if result.returncode != 0:
34
+ raise RuntimeError(f"plasmate binary not working: {result.stderr}")
35
+ except FileNotFoundError:
36
+ raise RuntimeError(
37
+ "plasmate binary not found. Install with: cargo install plasmate\n"
38
+ "Or: curl -fsSL https://plasmate.app/install.sh | sh"
39
+ )
40
+
41
+ def extract(self, url: str) -> dict:
42
+ """Fetch a URL and return parsed SOM output.
43
+
44
+ Returns the full SOM dict with regions, elements, meta, etc.
45
+ """
46
+ result = subprocess.run(
47
+ [self.plasmate_bin, "fetch", url],
48
+ capture_output=True, text=True, timeout=30
49
+ )
50
+ if result.returncode != 0:
51
+ raise RuntimeError(f"plasmate fetch failed: {result.stderr}")
52
+ return json.loads(result.stdout)
53
+
54
+ async def extract_async(self, url: str) -> dict:
55
+ """Async version of extract."""
56
+ proc = await asyncio.create_subprocess_exec(
57
+ self.plasmate_bin, "fetch", url,
58
+ stdout=asyncio.subprocess.PIPE,
59
+ stderr=asyncio.subprocess.PIPE
60
+ )
61
+ stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30)
62
+ if proc.returncode != 0:
63
+ raise RuntimeError(f"plasmate fetch failed: {stderr.decode()}")
64
+ return json.loads(stdout.decode())
65
+
66
+ def extract_markdown(self, url: str) -> str:
67
+ """Fetch a URL and return SOM content as markdown.
68
+
69
+ This is the simplest integration -- just get readable text
70
+ with structure preserved.
71
+ """
72
+ som_data = self.extract(url)
73
+ som = parse_som(som_data)
74
+ return to_markdown(som)
75
+
76
+ async def extract_markdown_async(self, url: str) -> str:
77
+ """Async version of extract_markdown."""
78
+ som_data = await self.extract_async(url)
79
+ som = parse_som(som_data)
80
+ return to_markdown(som)
81
+
82
+ def get_page_context(self, url: str) -> str:
83
+ """Get a token-efficient page context string for LLM consumption.
84
+
85
+ Returns a formatted string with:
86
+ - Page title and URL
87
+ - Interactive elements (what the agent can do)
88
+ - Content summary
89
+ - Compression stats
90
+ """
91
+ som_data = self.extract(url)
92
+ return self._build_context(som_data)
93
+
94
+ async def get_page_context_async(self, url: str) -> str:
95
+ """Async version of get_page_context."""
96
+ som_data = await self.extract_async(url)
97
+ return self._build_context(som_data)
98
+
99
+ def _build_context(self, som_data: dict) -> str:
100
+ """Build the LLM context string from raw SOM data."""
101
+ som = parse_som(som_data)
102
+
103
+ lines = []
104
+ lines.append(f"# {som.title}")
105
+ lines.append(f"URL: {som.url}")
106
+ lines.append(f"Language: {som.lang}")
107
+ lines.append("")
108
+
109
+ # Interactive elements
110
+ interactive = get_interactive_elements(som)
111
+ if interactive:
112
+ lines.append(f"## Interactive Elements ({len(interactive)})")
113
+ for el in interactive:
114
+ actions = ", ".join(el.actions) if el.actions else ""
115
+ label = el.text or el.label or (el.attrs.get("placeholder", "") if el.attrs else "")
116
+ lines.append(f' [{el.id}] {el.role} "{label}" ({actions})')
117
+ lines.append("")
118
+
119
+ # Links
120
+ links = get_links(som)
121
+ if links:
122
+ lines.append(f"## Links ({len(links)})")
123
+ for link in links[:20]: # Cap at 20
124
+ lines.append(f" [{link['id']}] {link['text']} -> {link['href']}")
125
+ if len(links) > 20:
126
+ lines.append(f" ... and {len(links) - 20} more")
127
+ lines.append("")
128
+
129
+ # Content
130
+ text = get_text(som)
131
+ if text:
132
+ lines.append("## Content")
133
+ lines.append(text[:2000]) # Cap content
134
+ if len(text) > 2000:
135
+ lines.append(f"... ({len(text) - 2000} more characters)")
136
+
137
+ # Stats
138
+ meta = som.meta
139
+ ratio = meta.html_bytes / max(meta.som_bytes, 1)
140
+ lines.append("")
141
+ lines.append("---")
142
+ lines.append(f"Compression: {ratio:.1f}x ({meta.html_bytes} HTML bytes -> {meta.som_bytes} SOM bytes)")
143
+ lines.append(f"Elements: {meta.element_count} ({meta.interactive_count} interactive)")
144
+
145
+ return "\n".join(lines)
@@ -0,0 +1,64 @@
1
+ """Helper functions for the Plasmate Browser Use integration."""
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ def estimate_tokens(text: str) -> int:
7
+ """Estimate token count for a string (roughly 4 chars per token)."""
8
+ return len(text.encode("utf-8")) // 4
9
+
10
+
11
+ def token_count_comparison(
12
+ som_data: dict[str, Any],
13
+ som_text: Optional[str] = None,
14
+ ) -> dict[str, Any]:
15
+ """Compare token counts between raw HTML and SOM representation.
16
+
17
+ Useful for benchmarking and demonstrating the token savings Plasmate
18
+ provides over traditional browser backends.
19
+
20
+ Args:
21
+ som_data: A SOM document dict (as returned by Plasmate).
22
+ som_text: Pre-rendered SOM text. If not provided, token estimate
23
+ is based on the SOM bytes from metadata.
24
+
25
+ Returns:
26
+ Dict with token counts and savings ratio::
27
+
28
+ {
29
+ "html_bytes": 87234,
30
+ "som_bytes": 4521,
31
+ "html_tokens_est": 21808,
32
+ "som_tokens_est": 1130,
33
+ "byte_ratio": 19.3,
34
+ "token_ratio": 19.3,
35
+ "token_savings_pct": 94.8,
36
+ }
37
+ """
38
+ meta = som_data.get("meta", {})
39
+ html_bytes = meta.get("html_bytes", 0)
40
+ som_bytes = meta.get("som_bytes", 0)
41
+
42
+ if som_text is not None:
43
+ som_text_bytes = len(som_text.encode("utf-8"))
44
+ else:
45
+ som_text_bytes = som_bytes
46
+
47
+ # Token estimation: ~4 chars per token (standard heuristic)
48
+ html_tokens = html_bytes // 4
49
+ som_tokens = som_text_bytes // 4
50
+
51
+ byte_ratio = html_bytes / som_bytes if som_bytes else 0
52
+ token_ratio = html_tokens / som_tokens if som_tokens else 0
53
+ savings_pct = (1 - som_tokens / html_tokens) * 100 if html_tokens else 0
54
+
55
+ return {
56
+ "html_bytes": html_bytes,
57
+ "som_bytes": som_bytes,
58
+ "som_text_bytes": som_text_bytes,
59
+ "html_tokens_est": html_tokens,
60
+ "som_tokens_est": som_tokens,
61
+ "byte_ratio": round(byte_ratio, 1),
62
+ "token_ratio": round(token_ratio, 1),
63
+ "token_savings_pct": round(savings_pct, 1),
64
+ }
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "plasmate-browser-use"
7
+ version = "0.3.0"
8
+ description = "Plasmate SOM integration for Browser Use - 10x fewer tokens for AI web agents"
9
+ readme = "README.md"
10
+ license = "Apache-2.0"
11
+ requires-python = ">=3.11"
12
+ dependencies = [
13
+ "som-parser>=0.3.0",
14
+ ]
15
+
16
+ [project.optional-dependencies]
17
+ browser-use = ["browser-use>=0.12.0"]
18
+
19
+ [project.urls]
20
+ Homepage = "https://plasmate.app"
21
+ Documentation = "https://plasmate.app/docs/integration-browser-use"
22
+ Repository = "https://github.com/plasmate-labs/plasmate"
23
+
24
+ [tool.hatch.build.targets.wheel]
25
+ packages = ["plasmate_browser_use"]