Skip to content

Commit b7729fe

Browse files
committed
Expose stream and watch
1 parent 55e429b commit b7729fe

File tree

8 files changed

+92
-51
lines changed

8 files changed

+92
-51
lines changed

.fernignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Specify files that shouldn't be modified by Fern
22
src/wrapper/
3+
src/index.ts
34
examples/
45
.vscode/
56

README.md

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,42 @@ for (const post of result.parsed.posts) {
5757

5858
### Streaming Agent Updates
5959

60+
> You can use the `stream` method to get the latest step taken on every change.
61+
6062
```ts
6163
const task = await browseruse.tasks.createTask({
6264
task: "Search for the top 10 Hacker News posts and return the title and url.",
6365
schema: TaskOutput,
6466
});
6567

66-
for await (const msg of task.stream()) {
67-
switch (msg.status) {
68-
case "started":
69-
case "paused":
70-
case "stopped":
71-
console.log(`running: ${msg}`);
72-
break;
73-
74-
case "finished":
75-
console.log(`done:`);
76-
77-
for (const post of msg.parsed.posts) {
78-
console.log(`${post.title} - ${post.url}`);
79-
}
80-
break;
68+
for await (const step of task.stream()) {
69+
console.log(step);
70+
}
71+
72+
const result = await task.complete();
73+
74+
for (const post of result.parsed.posts) {
75+
console.log(`${post.title} - ${post.url}`);
76+
}
77+
```
78+
79+
### Watching Agent Updates
80+
81+
> You can use the `watch` method to get the latest update on every change.
82+
83+
```ts
84+
const task = await browseruse.tasks.createTask({
85+
task: "Search for the top 10 Hacker News posts and return the title and url.",
86+
schema: TaskOutput,
87+
});
88+
89+
for await (const update of task.watch()) {
90+
console.log(update);
91+
92+
if (update.data.status === "finished") {
93+
for (const post of update.data.parsed.posts) {
94+
console.log(`${post.title} - ${post.url}`);
95+
}
8196
}
8297
}
8398
```

examples/retrieve.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ env();
1111
// gets API Key from environment variable BROWSER_USE_API_KEY
1212
const browseruse = new BrowserUseClient({
1313
apiKey: process.env.BROWSER_USE_API_KEY!,
14-
environment: "https://api.browser-use.com/api/v2",
1514
});
1615

1716
// Basic ---------------------------------------------------------------------
@@ -23,7 +22,8 @@ async function basic() {
2322
// Create Task
2423
const rsp = await browseruse.tasks.createTask({
2524
task: "What's the weather in SF and what's the temperature?",
26-
agent: { llm: "gemini-2.5-flash" },
25+
llm: "gemini-2.5-flash",
26+
schema: TaskOutput,
2727
});
2828

2929
poll: do {
@@ -67,7 +67,7 @@ async function structured() {
6767
const rsp = await browseruse.tasks.createTask({
6868
task: "What's the weather in SF and what's the temperature?",
6969
schema: TaskOutput,
70-
agent: { llm: "gpt-4.1" },
70+
llm: "gpt-4.1",
7171
});
7272

7373
poll: do {

examples/run.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ env();
1010
// gets API Key from environment variable BROWSER_USE_API_KEY
1111
const browseruse = new BrowserUseClient({
1212
apiKey: process.env.BROWSER_USE_API_KEY!,
13-
environment: "https://api.browser-use.com/api/v2",
1413
});
1514

1615
// Basic ---------------------------------------------------------------------
@@ -48,7 +47,7 @@ async function structured() {
4847
const rsp = await browseruse.tasks.createTask({
4948
task: "Search for the top 10 Hacker News posts and return the title and url!",
5049
schema: TaskOutput,
51-
agent: { llm: "gpt-4.1" },
50+
llm: "gpt-4.1",
5251
});
5352

5453
const result = await rsp.complete();

examples/stream.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ env();
1010

1111
const browseruse = new BrowserUseClient({
1212
apiKey: process.env.BROWSER_USE_API_KEY!,
13-
environment: "https://api.browser-use.com/api/v2",
1413
});
1514

1615
// Basic ---------------------------------------------------------------------
@@ -19,20 +18,45 @@ async function basic() {
1918
console.log("Basic: Creating task and starting stream...");
2019

2120
const task = await browseruse.tasks.createTask({
22-
task: "What's the weather in SF and what's the temperature?",
23-
agent: { llm: "gemini-2.5-flash" },
21+
task: "What's the weather and temperature in SF, NY, and LA?",
22+
llm: "gemini-2.5-flash",
2423
});
2524

26-
for await (const msg of task.stream()) {
27-
console.log(msg);
25+
console.log(`task.id: ${task.id}`);
26+
27+
const counter = { current: 0 };
28+
29+
for await (const step of task.stream()) {
30+
console.log(`STREAM 1: ${step.number}`);
31+
32+
counter.current++;
33+
34+
if (counter.current === 2) {
35+
break;
36+
}
37+
}
38+
39+
for await (const step of task.stream()) {
40+
counter.current++;
41+
42+
console.log(`STREAM 2: ${step.number}`);
2843
}
2944

3045
const result = await task.complete();
3146

47+
if (counter.current <= result.steps.length || counter.current !== result.steps.length + 2) {
48+
console.log(`counter.current: ${counter.current}, result.steps.length: ${result.steps.length}`);
49+
throw new Error(
50+
"Basic: Stream does not run as expected! Each step should be relogged whenever stream restarts!",
51+
);
52+
}
53+
3254
console.log("Basic: Stream completed");
3355
console.log(result.output);
3456
}
3557

58+
basic().catch(console.error);
59+
3660
// Structured ----------------------------------------------------------------
3761

3862
// Define Structured Output Schema
@@ -51,8 +75,8 @@ async function structured() {
5175

5276
const task = await browseruse.tasks.createTask({
5377
task: "Extract top 10 Hacker News posts and return the title, url, and score",
78+
llm: "gpt-4.1",
5479
schema: TaskOutput,
55-
agent: { llm: "gpt-4.1" },
5680
});
5781

5882
for await (const msg of task.stream()) {
@@ -68,6 +92,6 @@ async function structured() {
6892
}
6993
}
7094

71-
basic()
72-
.then(() => structured())
73-
.catch(console.error);
95+
// basic()
96+
// .then(() => structured())
97+
// .catch(console.error);

examples/watch.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ env();
1010
// gets API Key from environment variable BROWSER_USE_API_KEY
1111
const browseruse = new BrowserUseClient({
1212
apiKey: process.env.BROWSER_USE_API_KEY!,
13-
environment: "https://api.browser-use.com/api/v2",
1413
});
1514

1615
// Basic ---------------------------------------------------------------------
@@ -20,7 +19,7 @@ async function basic() {
2019

2120
const task = await browseruse.tasks.createTask({
2221
task: "What's the weather in SF and what's the temperature?",
23-
agent: { llm: "gemini-2.5-flash" },
22+
llm: "gemini-2.5-flash",
2423
});
2524

2625
for await (const msg of task.watch()) {
@@ -53,7 +52,7 @@ async function structured() {
5352
const task = await browseruse.tasks.createTask({
5453
task: "Extract top 10 Hacker News posts and return the title, url, and score",
5554
schema: TaskOutput,
56-
agent: { llm: "gpt-4.1" },
55+
llm: "gpt-4.1",
5756
});
5857

5958
for await (const msg of task.watch()) {

src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * as BrowserUse from "./api/index.js";
22
export { BrowserUseError, BrowserUseTimeoutError } from "./errors/index.js";
3-
export { BrowserUseClient } from "./Client.js";
43
export { BrowserUseEnvironment } from "./environments.js";
4+
export { BrowserUseClient } from "./wrapper/BrowserUseClient.js";
5+
export type { WrappedTaskFnsWithoutSchema } from "./wrapper/lib/parse.js";
6+
export type { WrappedTaskFnsWithSchema } from "./wrapper/lib/parse.js";

src/wrapper/lib/parse.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,25 @@ export function wrapCreateTaskResponse(
169169
} while (true);
170170
}
171171

172+
/**
173+
* Streams the steps of the task and closes when the task is finished.
174+
*
175+
* @description Logs each step of the task exactly once. If you start the stream again, it will log the steps again.
176+
*/
177+
async function* stream(
178+
config?: { interval?: number },
179+
options?: RequestOptions,
180+
): AsyncGenerator<BrowserUse.TaskStepView> {
181+
const steps: { total: number } = { total: 0 };
182+
183+
for await (const msg of _watch(response.id, config, options)) {
184+
for (let i = steps.total; i < msg.data.steps.length; i++) {
185+
yield msg.data.steps[i] satisfies BrowserUse.TaskStepView;
186+
}
187+
steps.total = msg.data.steps.length;
188+
}
189+
}
190+
172191
function watch<T extends ZodType>(
173192
schema: T,
174193
config?: PollConfig,
@@ -252,24 +271,6 @@ export function wrapCreateTaskResponse(
252271
throw new Error("Task did not finish");
253272
}
254273

255-
async function* stream(
256-
config?: { interval?: number },
257-
options?: RequestOptions,
258-
): AsyncGenerator<BrowserUse.TaskStepView> {
259-
const step: { current: number } = { current: 0 };
260-
261-
const interval = config?.interval ?? 2000;
262-
263-
for await (const msg of _watch(response.id, { interval }, options)) {
264-
if (msg.data.steps.length > step.current) {
265-
step.current = msg.data.steps.length;
266-
267-
const lastStepIdx = msg.data.steps.length - 1;
268-
yield msg.data.steps[lastStepIdx] satisfies BrowserUse.TaskStepView;
269-
}
270-
}
271-
}
272-
273274
// NOTE: Finally, we return the wrapped task response.
274275

275276
if (schema == null) {

0 commit comments

Comments
 (0)