1
+ import asyncio
1
2
import hashlib
2
3
import json
4
+ import time
3
5
import typing
4
6
from datetime import datetime
5
7
from typing import Any , AsyncIterator , Generic , Iterator , Type , TypeVar , Union
@@ -33,47 +35,52 @@ def default(self, o: Any) -> Any: # type: ignore[override]
33
35
return super ().default (o )
34
36
35
37
36
- def hash_task_view (task_view : TaskView ) -> str :
38
+ def _hash_task_view (task_view : TaskView ) -> str :
37
39
"""Hashes the task view to detect changes."""
38
40
return hashlib .sha256 (
39
41
json .dumps (task_view .model_dump (), sort_keys = True , cls = CustomJSONEncoder ).encode ()
40
42
).hexdigest ()
41
43
42
44
43
- # def _watch(
44
- # self,
45
- # task_id: str,
46
- # interval: float = 1, request_options:typing.Optional[RequestOptions] = None,
47
- # *,
48
- # # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
49
- # # The extra values given here take precedence over values defined on the client or passed to this method.
50
- # request_options: typing.Optional[RequestOptions] = None,
51
- # ) -> Iterator[TaskView]:
52
- # """Converts a polling loop into a generator loop."""
53
- # hash: str | None = None
45
+ def _parse_task_view_with_output (task_view : TaskView , schema : Type [T ]) -> TaskViewWithOutput [T ]:
46
+ """Parses the task view with output."""
47
+ if task_view .output is None :
48
+ return TaskViewWithOutput [T ](** task_view .model_dump (), parsed_output = None )
54
49
55
- # while True:
56
- # res = self.retrieve(
57
- # task_id=task_id,
58
- # extra_headers=extra_headers,
59
- # extra_query=extra_query,
60
- # extra_body=extra_body,
61
- # timeout=timeout,
62
- # )
50
+ return TaskViewWithOutput [T ](** task_view .model_dump (), parsed_output = schema .model_validate_json (task_view .output ))
63
51
64
- # res_hash = hash_task_view(res)
65
52
66
- # if hash is None or res_hash != hash:
67
- # hash = res_hash
68
- # yield res
53
+ # Sync -----------------------------------------------------------------------
69
54
70
- # if res.status == "finished":
71
- # break
72
55
73
- # time.sleep(interval)
56
+ def _watch (
57
+ client : TasksClient , task_id : str , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
58
+ ) -> Iterator [TaskViewWithOutput [T ]]:
59
+ """Yields the latest task state on every change."""
60
+ hash : str | None = None
61
+ while True :
62
+ res = client .get_task (task_id , request_options = request_options )
63
+ res_hash = _hash_task_view (res )
74
64
65
+ if hash is None or res_hash != hash :
66
+ hash = res_hash
67
+ yield res
68
+
69
+ if res .status == "finished" or res .status == "stopped" or res .status == "paused" :
70
+ break
71
+
72
+ time .sleep (interval )
75
73
76
- # Sync -----------------------------------------------------------------------
74
+
75
+ def _stream (
76
+ client : TasksClient , task_id : str , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
77
+ ) -> Iterator [TaskStepView ]:
78
+ """Streams the steps of the task and closes when the task is finished."""
79
+ total_steps = 0
80
+ for state in _watch (client , task_id , interval , request_options ):
81
+ for i in range (total_steps , len (state .steps )):
82
+ total_steps = i + 1
83
+ yield state .steps [i ]
77
84
78
85
79
86
class WrappedTaskCreatedResponse (TaskCreatedResponse ):
@@ -87,21 +94,23 @@ def complete(
87
94
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
88
95
) -> TaskViewWithOutput [T ]:
89
96
"""Waits for the task to finish and return the result."""
90
- pass
97
+ for state in _watch (self ._client , self .id , interval , request_options ):
98
+ if state .status == "finished" or state .status == "stopped" or state .status == "paused" :
99
+ return state
100
+
101
+ raise Exception ("Iterator ended without finding a finished state!" )
91
102
92
103
def stream (
93
104
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
94
105
) -> Iterator [TaskStepView ]:
95
106
"""Streams the steps of the task and closes when the task is finished."""
96
- for i in range (10 ):
97
- yield TaskStepView (number = i , status = "finished" )
107
+ return _stream (self ._client , self .id , interval , request_options )
98
108
99
109
def watch (
100
110
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
101
111
) -> Iterator [TaskViewWithOutput [T ]]:
102
112
"""Yields the latest task state on every change."""
103
- for i in range (10 ):
104
- yield TaskViewWithOutput [T ](status = "finished" )
113
+ return _watch (self ._client , self .id , interval , request_options )
105
114
106
115
107
116
# Structured
@@ -120,26 +129,58 @@ def complete(
120
129
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
121
130
) -> TaskViewWithOutput [T ]:
122
131
"""Waits for the task to finish and return the result."""
123
- pass
132
+ for state in _watch (self ._client , self .id , interval , request_options ):
133
+ if state .status == "finished" or state .status == "stopped" or state .status == "paused" :
134
+ return _parse_task_view_with_output (state , self ._schema )
135
+
136
+ raise Exception ("Iterator ended without finding a finished state!" )
124
137
125
138
def stream (
126
139
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
127
140
) -> Iterator [TaskStepView ]:
128
141
"""Streams the steps of the task and closes when the task is finished."""
129
- for i in range (10 ):
130
- yield TaskStepView (number = i , status = "finished" )
142
+ return _stream (self ._client , self .id , interval , request_options )
131
143
132
144
def watch (
133
145
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
134
146
) -> Iterator [TaskViewWithOutput [T ]]:
135
147
"""Yields the latest task state on every change."""
136
- for i in range ( 10 ):
137
- yield TaskViewWithOutput [ T ]( status = "finished" )
148
+ for state in _watch ( self . _client , self . id , interval , request_options ):
149
+ yield _parse_task_view_with_output ( state , self . _schema )
138
150
139
151
140
152
# Async ----------------------------------------------------------------------
141
153
142
154
155
+ async def _async_watch (
156
+ client : AsyncTasksClient , task_id : str , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
157
+ ) -> AsyncIterator [TaskViewWithOutput [T ]]:
158
+ """Yields the latest task state on every change."""
159
+ hash : str | None = None
160
+ while True :
161
+ res = await client .get_task (task_id , request_options = request_options )
162
+ res_hash = _hash_task_view (res )
163
+ if hash is None or res_hash != hash :
164
+ hash = res_hash
165
+ yield res
166
+
167
+ if res .status == "finished" or res .status == "stopped" or res .status == "paused" :
168
+ break
169
+
170
+ await asyncio .sleep (interval )
171
+
172
+
173
+ async def _async_stream (
174
+ client : AsyncTasksClient , task_id : str , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
175
+ ) -> AsyncIterator [TaskStepView ]:
176
+ """Streams the steps of the task and closes when the task is finished."""
177
+ total_steps = 0
178
+ for state in _async_watch (client , task_id , interval , request_options ):
179
+ for i in range (total_steps , len (state .steps )):
180
+ total_steps = i + 1
181
+ yield state .steps [i ]
182
+
183
+
143
184
class AsyncWrappedTaskCreatedResponse (TaskCreatedResponse ):
144
185
"""TaskCreatedResponse with utility methods for easier interfacing with Browser Use Cloud."""
145
186
@@ -149,21 +190,23 @@ def __init__(self, id: str, client: AsyncTasksClient):
149
190
150
191
async def complete (self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None ) -> TaskView :
151
192
"""Waits for the task to finish and return the result."""
152
- pass
193
+ for state in _async_watch (self ._client , self .id , interval , request_options ):
194
+ if state .status == "finished" or state .status == "stopped" or state .status == "paused" :
195
+ return state
196
+
197
+ raise Exception ("Iterator ended without finding a finished state!" )
153
198
154
199
async def stream (
155
200
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
156
201
) -> AsyncIterator [TaskStepView ]:
157
202
"""Streams the steps of the task and closes when the task is finished."""
158
- for i in range (10 ):
159
- yield TaskStepView (number = i , status = "finished" )
203
+ return _async_stream (self ._client , self .id , interval , request_options )
160
204
161
205
async def watch (
162
206
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
163
207
) -> AsyncIterator [TaskView ]:
164
208
"""Yields the latest task state on every change."""
165
- for i in range (10 ):
166
- yield TaskView (status = "finished" )
209
+ return _async_watch (self ._client , self .id , interval , request_options )
167
210
168
211
169
212
# Structured
@@ -182,18 +225,21 @@ async def complete(
182
225
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
183
226
) -> TaskViewWithOutput [T ]:
184
227
"""Waits for the task to finish and return the result."""
185
- pass
228
+ for state in _async_watch (self ._client , self .id , interval , request_options ):
229
+ if state .status == "finished" or state .status == "stopped" or state .status == "paused" :
230
+ return _parse_task_view_with_output (state , self ._schema )
231
+
232
+ raise Exception ("Iterator ended without finding a finished state!" )
186
233
187
234
async def stream (
188
235
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
189
236
) -> AsyncIterator [TaskStepView ]:
190
237
"""Streams the steps of the task and closes when the task is finished."""
191
- for i in range (10 ):
192
- yield TaskStepView (number = i , status = "finished" )
238
+ return _async_stream (self ._client , self .id , interval , request_options )
193
239
194
240
async def watch (
195
241
self , interval : float = 1 , request_options : typing .Optional [RequestOptions ] = None
196
242
) -> AsyncIterator [TaskViewWithOutput [T ]]:
197
243
"""Yields the latest task state on every change."""
198
- for i in range ( 10 ):
199
- yield TaskViewWithOutput [ T ]( status = "finished" )
244
+ for state in _async_watch ( self . _client , self . id , interval , request_options ):
245
+ yield _parse_task_view_with_output ( state , self . _schema )
0 commit comments