frdel commited on
Commit
bedc13b
·
1 Parent(s): fba8c75

browser use upgrade

Browse files
Files changed (4) hide show
  1. models.py +69 -20
  2. python/tools/browser_agent.py +2 -1
  3. requirements.txt +2 -2
  4. run_ui.py +3 -1
models.py CHANGED
@@ -19,7 +19,7 @@ import litellm
19
  import openai
20
 
21
  from python.helpers import dotenv
22
- from python.helpers import settings
23
  from python.helpers.dotenv import load_dotenv
24
  from python.helpers.providers import get_provider_config
25
  from python.helpers.rate_limiter import RateLimiter
@@ -545,7 +545,27 @@ class LiteLLMChatWrapper(SimpleChatModel):
545
  await asyncio.sleep(retry_delay_s)
546
 
547
 
548
- class BrowserCompatibleChatWrapper(LiteLLMChatWrapper):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
549
  """
550
  A wrapper for browser agent that can filter/sanitize messages
551
  before sending them to the LLM.
@@ -553,32 +573,61 @@ class BrowserCompatibleChatWrapper(LiteLLMChatWrapper):
553
 
554
  def __init__(self, *args, **kwargs):
555
  turn_off_logging()
556
- super().__init__(*args, **kwargs)
 
557
  # Browser-use may expect a 'model' attribute
558
- self.model = self.model_name
 
559
 
560
- def _call(
561
- self,
562
- messages: List[BaseMessage],
563
- stop: Optional[List[str]] = None,
564
- run_manager: Optional[CallbackManagerForLLMRun] = None,
565
- **kwargs: Any,
566
- ) -> str:
567
- turn_off_logging()
568
- result = super()._call(messages, stop, run_manager, **kwargs)
569
- return result
570
 
571
- async def _astream(
 
 
 
 
 
 
 
572
  self,
573
  messages: List[BaseMessage],
574
  stop: Optional[List[str]] = None,
575
- run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
576
  **kwargs: Any,
577
- ) -> AsyncIterator[ChatGenerationChunk]:
578
- turn_off_logging()
579
- async for chunk in super()._astream(messages, stop, run_manager, **kwargs):
580
- yield chunk
581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
 
583
  class LiteLLMEmbeddingWrapper(Embeddings):
584
  model_name: str
 
19
  import openai
20
 
21
  from python.helpers import dotenv
22
+ from python.helpers import settings, dirty_json
23
  from python.helpers.dotenv import load_dotenv
24
  from python.helpers.providers import get_provider_config
25
  from python.helpers.rate_limiter import RateLimiter
 
545
  await asyncio.sleep(retry_delay_s)
546
 
547
 
548
+ class AsyncAIChatReplacement:
549
+ class _Completions:
550
+ def __init__(self, wrapper):
551
+ self._wrapper = wrapper
552
+
553
+ async def create(self, *args, **kwargs):
554
+ # call the async _acall method on the wrapper
555
+ return await self._wrapper._acall(*args, **kwargs)
556
+
557
+ class _Chat:
558
+ def __init__(self, wrapper):
559
+ self.completions = AsyncAIChatReplacement._Completions(wrapper)
560
+
561
+ def __init__(self, wrapper, *args, **kwargs):
562
+ self._wrapper = wrapper
563
+ self.chat = AsyncAIChatReplacement._Chat(wrapper)
564
+
565
+
566
+ from browser_use.llm import ChatOllama, ChatOpenRouter, ChatGoogle, ChatAnthropic, ChatGroq, ChatOpenAI
567
+
568
+ class BrowserCompatibleChatWrapper(ChatOpenRouter):
569
  """
570
  A wrapper for browser agent that can filter/sanitize messages
571
  before sending them to the LLM.
 
573
 
574
  def __init__(self, *args, **kwargs):
575
  turn_off_logging()
576
+ # Create the underlying LiteLLM wrapper
577
+ self._wrapper = LiteLLMChatWrapper(*args, **kwargs)
578
  # Browser-use may expect a 'model' attribute
579
+ self.model = self._wrapper.model_name
580
+ self.kwargs = self._wrapper.kwargs
581
 
582
+ @property
583
+ def model_name(self) -> str:
584
+ return self._wrapper.model_name
 
 
 
 
 
 
 
585
 
586
+ @property
587
+ def provider(self) -> str:
588
+ return self._wrapper.provider
589
+
590
+ def get_client(self, *args, **kwargs): # type: ignore
591
+ return AsyncAIChatReplacement(self, *args, **kwargs)
592
+
593
+ async def _acall(
594
  self,
595
  messages: List[BaseMessage],
596
  stop: Optional[List[str]] = None,
597
+ run_manager: Optional[CallbackManagerForLLMRun] = None,
598
  **kwargs: Any,
599
+ ):
600
+ # Apply rate limiting if configured
601
+ apply_rate_limiter_sync(self._wrapper.a0_model_conf, str(messages))
 
602
 
603
+ # Call the model
604
+ try:
605
+ model = kwargs.pop("model", None)
606
+ kwrgs = {**self._wrapper.kwargs, **kwargs}
607
+
608
+ # hack from browser-use to fix json schema for gemini
609
+ if "response_format" in kwrgs and "json_schema" in kwrgs["response_format"] and model.startswith("gemini/"):
610
+ kwrgs["response_format"]["json_schema"] = ChatGoogle("")._fix_gemini_schema(self._wrapper.kwargs)
611
+
612
+ resp = await acompletion(
613
+ model=self._wrapper.model_name,
614
+ messages=messages,
615
+ stop=stop,
616
+ **kwrgs,
617
+ )
618
+ except Exception as e:
619
+ raise e
620
+
621
+ # another hack for browser-use post process invalid jsons
622
+ try:
623
+ if "response_format" in kwrgs and "json_schema" in kwrgs["response_format"] or "json_object" in kwrgs["response_format"]:
624
+ if resp.choices[0].message.content is not None and not resp.choices[0].message.content.startswith("{"): # type: ignore
625
+ js = dirty_json.parse(resp.choices[0].message.content) # type: ignore
626
+ resp.choices[0].message.content = dirty_json.stringify(js) # type: ignore
627
+ except Exception as e:
628
+ pass
629
+
630
+ return resp
631
 
632
  class LiteLLMEmbeddingWrapper(Embeddings):
633
  model_name: str
python/tools/browser_agent.py CHANGED
@@ -148,6 +148,7 @@ class State:
148
  ),
149
  controller=controller,
150
  enable_memory=False, # Disable memory to avoid state conflicts
 
151
  sensitive_data=cast(dict[str, str | dict[str, str]] | None, secrets_dict or {}), # Pass secrets
152
  )
153
  except Exception as e:
@@ -387,7 +388,7 @@ class BrowserAgent(Tool):
387
  def get_use_agent_log(use_agent: browser_use.Agent | None):
388
  result = ["🚦 Starting task"]
389
  if use_agent:
390
- action_results = use_agent.state.history.action_results() or []
391
  short_log = []
392
  for item in action_results:
393
  # final results
 
148
  ),
149
  controller=controller,
150
  enable_memory=False, # Disable memory to avoid state conflicts
151
+ llm_timeout=3000, # TODO rem
152
  sensitive_data=cast(dict[str, str | dict[str, str]] | None, secrets_dict or {}), # Pass secrets
153
  )
154
  except Exception as e:
 
388
  def get_use_agent_log(use_agent: browser_use.Agent | None):
389
  result = ["🚦 Starting task"]
390
  if use_agent:
391
+ action_results = use_agent.history.action_results() or []
392
  short_log = []
393
  for item in action_results:
394
  # final results
requirements.txt CHANGED
@@ -1,6 +1,6 @@
1
  a2wsgi==1.10.8
2
  ansio==0.0.1
3
- browser-use==0.2.5
4
  docker==7.1.0
5
  duckduckgo-search==6.1.12
6
  faiss-cpu==1.11.0
@@ -19,7 +19,7 @@ langchain-unstructured[all-docs]==0.1.6
19
  openai-whisper==20240930
20
  lxml_html_clean==0.3.1
21
  markdown==3.7
22
- mcp==1.12.4
23
  newspaper3k==0.2.8
24
  paramiko==3.5.0
25
  playwright==1.52.0
 
1
  a2wsgi==1.10.8
2
  ansio==0.0.1
3
+ browser-use==0.5.11
4
  docker==7.1.0
5
  duckduckgo-search==6.1.12
6
  faiss-cpu==1.11.0
 
19
  openai-whisper==20240930
20
  lxml_html_clean==0.3.1
21
  markdown==3.7
22
+ mcp==1.13.1
23
  newspaper3k==0.2.8
24
  paramiko==3.5.0
25
  playwright==1.52.0
run_ui.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from datetime import timedelta
2
  import os
3
  import secrets
@@ -8,6 +9,7 @@ import struct
8
  from functools import wraps
9
  import threading
10
  from flask import Flask, request, Response, session, redirect, url_for, render_template_string
 
11
  import initialize
12
  from python.helpers import files, git, mcp_server, fasta2a_server
13
  from python.helpers.files import get_abs_path
@@ -217,7 +219,7 @@ def run():
217
  name = handler.__module__.split(".")[-1]
218
  instance = handler(app, lock)
219
 
220
- async def handler_wrap():
221
  return await instance.handle_request(request=request)
222
 
223
  if handler.requires_loopback():
 
1
+ import asyncio
2
  from datetime import timedelta
3
  import os
4
  import secrets
 
9
  from functools import wraps
10
  import threading
11
  from flask import Flask, request, Response, session, redirect, url_for, render_template_string
12
+ from werkzeug.wrappers.response import Response as BaseResponse
13
  import initialize
14
  from python.helpers import files, git, mcp_server, fasta2a_server
15
  from python.helpers.files import get_abs_path
 
219
  name = handler.__module__.split(".")[-1]
220
  instance = handler(app, lock)
221
 
222
+ async def handler_wrap() -> BaseResponse:
223
  return await instance.handle_request(request=request)
224
 
225
  if handler.requires_loopback():