Skip to content

Commit dfe0481

Browse files
committed
Add README Docs
1 parent 89b1c95 commit dfe0481

File tree

5 files changed

+314
-1
lines changed

5 files changed

+314
-1
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# 🤖 Browser Use Examples
2+
3+
> A collection of Browser Use SDK examples.
4+
5+
### TypeScript
6+
7+
- **[scrapper](./scrapper/README.md)**: A utility for extracting complex, scattered data about entities, saving results to a database, and providing live previews of scraping progress.
8+
- **[agent](./agent/README.md)**: Demonstrates how to create an agent that can browse the web and execute browser tasks using Browser Use, including integration with Vercel AI SDK and chat interfaces.
9+
- **[discord](./discord/README.md)**: An example Discord bot that uses the Browser Use Cloud API to execute browser tasks and interact with users via Discord commands.
10+
11+
### Python
12+
13+
- **[slack](./slack/README.md)**: An example Slack bot that listens for mentions and interacts with users using the Browser Use SDK, including setup for local development and required Slack permissions.

typescript/agent/README.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,142 @@
66
<img src="media/app.png" alt="App Screenshot" height="400" />
77
</p>
88

9+
## TLDR
10+
11+
### Integrating with Vercel AI SDK
12+
13+
> [`src/app/api/chat/route.ts`](./src/app/api/chat/route.ts)
14+
15+
```ts
16+
const bu = new BrowserUse({
17+
apiKey: process.env.BROWSER_USE_API_KEY,
18+
});
19+
20+
type TaskStatus =
21+
| {
22+
status: "starting";
23+
liveUrl: string | null;
24+
}
25+
| {
26+
status: "running";
27+
28+
lastStep: BrowserUse.Tasks.TaskStepView;
29+
liveUrl: string | null;
30+
}
31+
| {
32+
status: "done";
33+
34+
output: string;
35+
liveUrl: string;
36+
37+
sessionId: string;
38+
};
39+
40+
const tools = {
41+
runTask: tool({
42+
description: "Run a task in a web browser.",
43+
inputSchema: z.object({
44+
task: z.string(),
45+
}),
46+
async *execute({ task }, { abortSignal }) {
47+
// Create Task
48+
49+
const res = await bu.tasks.create({ task }, { signal: abortSignal });
50+
51+
const gen = bu.tasks.stream(res.id, { signal: abortSignal });
52+
53+
for await (const event of gen) {
54+
const status = event.data;
55+
56+
console.log(`[agent] task status: ${status.status}`);
57+
58+
if (status.doneOutput) {
59+
console.log(`[agent] task output: ${status.doneOutput}`);
60+
}
61+
62+
switch (status.status) {
63+
case "started":
64+
case "paused":
65+
case "stopped":
66+
if (status.steps == null || status.steps.length === 0) {
67+
yield {
68+
status: "starting",
69+
liveUrl: status.session.liveUrl ? status.session.liveUrl : null,
70+
} satisfies TaskStatus;
71+
72+
break;
73+
}
74+
75+
const lastStep = status.steps[status.steps.length - 1];
76+
77+
yield {
78+
status: "running",
79+
lastStep: lastStep,
80+
liveUrl: status.session.liveUrl ? status.session.liveUrl : null,
81+
} satisfies TaskStatus;
82+
83+
break;
84+
85+
case "finished":
86+
if (status.session.liveUrl == null || status.doneOutput == null) {
87+
break;
88+
}
89+
90+
yield {
91+
status: "done",
92+
output: status.doneOutput,
93+
liveUrl: status.session.liveUrl,
94+
sessionId: status.sessionId,
95+
} satisfies TaskStatus;
96+
97+
break;
98+
99+
default:
100+
throw new Error(`Unknown status: ${status.status}`);
101+
}
102+
}
103+
},
104+
}),
105+
} satisfies ToolSet;
106+
107+
export type ChatTools = InferUITools<typeof tools>;
108+
109+
export type ChatMessage = UIMessage<never, UIDataTypes, ChatTools>;
110+
111+
// ROUTE
112+
113+
export async function POST(req: Request) {
114+
const { messages, model }: { messages: UIMessage[]; model: string } = await req.json();
115+
116+
const result = streamText({
117+
model: model,
118+
messages: convertToModelMessages(messages),
119+
system:
120+
"You are a helpful assistant that can answer questions and help with tasks. You can use the tools provided to you to help you answer questions and help with tasks.",
121+
tools: tools,
122+
stopWhen: stepCountIs(15),
123+
});
124+
125+
// send sources and reasoning back to the client
126+
return result.toUIMessageStreamResponse({
127+
sendSources: false,
128+
sendReasoning: false,
129+
});
130+
}
131+
```
132+
133+
### Integrating with Vercel AI Chat
134+
135+
> [`src/app/page.tsx`](./src/app/page.tsx).
136+
137+
```tsx
138+
const { messages, sendMessage, status } = useChat<ChatMessage>({
139+
transport: new DefaultChatTransport({
140+
api: "/api/chat",
141+
}),
142+
});
143+
```
144+
9145
## Development Setup
10146

11147
```bash

typescript/agent/src/app/api/chat/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { z } from "zod";
1212
import { BrowserUse } from "browser-use-sdk";
1313

1414
// Allow streaming responses up to 5 minutes
15-
export const maxDuration = 300;
15+
export const maxDuration = 15 * 60;
1616

1717
const bu = new BrowserUse({
1818
apiKey: process.env.BROWSER_USE_API_KEY,

typescript/discord/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,88 @@
1010
<img src="media/message.png" alt="Bot Completion Message Screenshot" height="400" />
1111
</p>
1212

13+
## TLDR
14+
15+
### Creating a `/` Command
16+
17+
> [`src/commands/run.ts`](./src/commands/run.ts)
18+
19+
```ts
20+
const res = await browseruse.tasks.create({
21+
task: command,
22+
agentSettings: { llm: "o3" },
23+
});
24+
25+
const gen = browseruse.tasks.stream(res.id);
26+
27+
for await (const event of gen) {
28+
const status = event.data;
29+
30+
switch (status.status) {
31+
case "started":
32+
case "stopped":
33+
case "paused": {
34+
const liveUrl = status.session.liveUrl ?? "⏳ Waiting...";
35+
36+
const description: string[] = [];
37+
38+
description.push(`**Command:** ${command}`);
39+
description.push(`**Task ID:** ${status.id}`);
40+
41+
description.push("");
42+
43+
if (status.steps) {
44+
for (const step of status.steps) {
45+
description.push(`- [${step.url}] ${step.nextGoal}`);
46+
}
47+
} else {
48+
description.push("No steps yet");
49+
}
50+
51+
if (status.doneOutput) {
52+
description.push("");
53+
description.push(status.doneOutput);
54+
}
55+
56+
const embed = new EmbedBuilder()
57+
.setTitle("🤖 Browser Use Task")
58+
.setDescription(description.join("\n"))
59+
.setColor(0x0099ff)
60+
.addFields(
61+
{ name: "Status", value: "🔄 Running...", inline: true },
62+
{ name: "Live Session", value: liveUrl, inline: true },
63+
)
64+
.setTimestamp();
65+
66+
await interaction.editReply({ embeds: [embed] });
67+
68+
break;
69+
}
70+
71+
case "finished": {
72+
const output: string[] = [];
73+
74+
output.push(`# Browser Use Task - ${status.id} ✅`);
75+
output.push(`## Task`);
76+
output.push(command);
77+
78+
output.push("");
79+
80+
output.push(`## Output`);
81+
output.push(status.doneOutput ?? "No output");
82+
83+
await interaction.editReply({ content: output.join("\n"), embeds: [] });
84+
85+
break;
86+
}
87+
default:
88+
throw new ExhaustiveSwitchCheck(status.status);
89+
}
90+
91+
console.log(`[${status.id}] ${status.status}`);
92+
}
93+
```
94+
1395
## Development Setup
1496

1597
```bash

typescript/scrapper/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,88 @@
1111
<img src="media/live.png" width="600" alt="Browser Use Scrapper" />
1212
</p>
1313

14+
## TLDR
15+
16+
### Saving Result to Database
17+
18+
> [`src/app/api/webhook/route.ts`](./src/app/api/webhook/route.ts)
19+
20+
```ts
21+
export async function POST(request: Request) {
22+
const body = await request.text();
23+
24+
const signature = request.headers.get("x-browser-use-signature") as string;
25+
const timestamp = request.headers.get("x-browser-use-timestamp") as string;
26+
27+
const event = await verifyWebhookEventSignature(
28+
{
29+
body,
30+
signature,
31+
timestamp,
32+
},
33+
{
34+
secret: process.env.SECRET_KEY!,
35+
}
36+
);
37+
38+
if (event.ok) {
39+
switch (event.event.type) {
40+
case "test":
41+
break;
42+
case "agent.task.status_update":
43+
break;
44+
45+
default:
46+
throw new ExhaustiveSwitchCheck(event.event);
47+
}
48+
}
49+
50+
return new Response("OK");
51+
}
52+
```
53+
54+
### Integrating Live Preview
55+
56+
> [`src/app/api/scrape/[id]/route.ts`](./src/app/api/scrape/[id]/route.ts)
57+
58+
```tsx
59+
export async function GET(
60+
request: Request,
61+
{ params }: { params: Promise<{ id: string }> }
62+
) {
63+
const { id } = await params;
64+
65+
if (typeof id !== "string") {
66+
return new Response("Bad request", { status: 400 });
67+
}
68+
69+
const entry = await db.query.profiles.findFirst({
70+
where: eq(schema.profiles.id, id),
71+
});
72+
73+
if (entry == null) {
74+
return new Response("Not found", { status: 404 });
75+
}
76+
77+
// Create Task
78+
const gen = browseruse.tasks.stream({
79+
taskId: entry.browserUseTaskId,
80+
schema: zScrapperSchema,
81+
});
82+
83+
const stream = gtos(gen, {});
84+
85+
return new Response(stream, {
86+
headers: {
87+
"Content-Type": "text/event-stream; charset=utf-8",
88+
"Cache-Control": "no-cache, no-transform",
89+
Connection: "keep-alive",
90+
"X-Accel-Buffering": "no", // avoid proxy buffering
91+
},
92+
});
93+
}
94+
```
95+
1496
## Developer Guide
1597

1698
```sh

0 commit comments

Comments
 (0)