camel-ai 0.2.71a7__py3-none-any.whl → 0.2.71a9__py3-none-any.whl

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.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

@@ -14,6 +14,8 @@
14
14
  import asyncio
15
15
  from typing import TYPE_CHECKING, Any, Dict, Optional
16
16
 
17
+ from .config_loader import ConfigLoader
18
+
17
19
  if TYPE_CHECKING:
18
20
  from playwright.async_api import Page
19
21
 
@@ -21,15 +23,24 @@ if TYPE_CHECKING:
21
23
  class ActionExecutor:
22
24
  r"""Executes high-level actions (click, type …) on a Playwright Page."""
23
25
 
24
- # Configuration constants
25
- DEFAULT_TIMEOUT = 5000 # 5 seconds
26
- SHORT_TIMEOUT = 2000 # 2 seconds
27
- MAX_SCROLL_AMOUNT = 5000 # Maximum scroll distance in pixels
28
-
29
- def __init__(self, page: "Page", session: Optional[Any] = None):
26
+ def __init__(
27
+ self,
28
+ page: "Page",
29
+ session: Optional[Any] = None,
30
+ default_timeout: Optional[int] = None,
31
+ short_timeout: Optional[int] = None,
32
+ max_scroll_amount: Optional[int] = None,
33
+ ):
30
34
  self.page = page
31
35
  self.session = session # HybridBrowserSession instance
32
36
 
37
+ # Configure timeouts using the config file with optional overrides
38
+ self.default_timeout = ConfigLoader.get_action_timeout(default_timeout)
39
+ self.short_timeout = ConfigLoader.get_short_timeout(short_timeout)
40
+ self.max_scroll_amount = ConfigLoader.get_max_scroll_amount(
41
+ max_scroll_amount
42
+ )
43
+
33
44
  # ------------------------------------------------------------------
34
45
  # Public helpers
35
46
  # ------------------------------------------------------------------
@@ -139,7 +150,7 @@ class ActionExecutor:
139
150
  try:
140
151
  if self.session:
141
152
  async with self.page.context.expect_page(
142
- timeout=self.SHORT_TIMEOUT
153
+ timeout=self.short_timeout
143
154
  ) as new_page_info:
144
155
  await element.click(modifiers=["ControlOrMeta"])
145
156
  new_page = await new_page_info.value
@@ -188,7 +199,7 @@ class ActionExecutor:
188
199
 
189
200
  # Fallback to normal force click if ctrl+click fails
190
201
  try:
191
- await element.click(force=True, timeout=self.DEFAULT_TIMEOUT)
202
+ await element.click(force=True, timeout=self.default_timeout)
192
203
  details["click_method"] = "playwright_force_click"
193
204
  return {
194
205
  "message": f"Fallback clicked element: {found_selector}",
@@ -224,7 +235,7 @@ class ActionExecutor:
224
235
  }
225
236
 
226
237
  try:
227
- await self.page.fill(target, text, timeout=self.SHORT_TIMEOUT)
238
+ await self.page.fill(target, text, timeout=self.short_timeout)
228
239
  return {
229
240
  "message": f"Typed '{text}' into {target}",
230
241
  "details": details,
@@ -254,7 +265,7 @@ class ActionExecutor:
254
265
 
255
266
  try:
256
267
  await self.page.select_option(
257
- target, value, timeout=self.DEFAULT_TIMEOUT
268
+ target, value, timeout=self.default_timeout
258
269
  )
259
270
  return {
260
271
  "message": f"Selected '{value}' in {target}",
@@ -283,7 +294,7 @@ class ActionExecutor:
283
294
  details["wait_type"] = "selector"
284
295
  details["selector"] = sel
285
296
  await self.page.wait_for_selector(
286
- sel, timeout=self.DEFAULT_TIMEOUT
297
+ sel, timeout=self.default_timeout
287
298
  )
288
299
  return {"message": f"Waited for {sel}", "details": details}
289
300
  return {
@@ -303,7 +314,7 @@ class ActionExecutor:
303
314
  target = f"[aria-ref='{ref}']"
304
315
  details = {"ref": ref, "target": target}
305
316
 
306
- await self.page.wait_for_selector(target, timeout=self.DEFAULT_TIMEOUT)
317
+ await self.page.wait_for_selector(target, timeout=self.default_timeout)
307
318
  txt = await self.page.text_content(target)
308
319
 
309
320
  details["extracted_text"] = txt
@@ -337,9 +348,9 @@ class ActionExecutor:
337
348
  # Safely convert amount to integer and clamp to reasonable range
338
349
  amount_int = int(amount)
339
350
  amount_int = max(
340
- -self.MAX_SCROLL_AMOUNT,
341
- min(self.MAX_SCROLL_AMOUNT, amount_int),
342
- ) # Clamp to MAX_SCROLL_AMOUNT range
351
+ -self.max_scroll_amount,
352
+ min(self.max_scroll_amount, amount_int),
353
+ ) # Clamp to max_scroll_amount range
343
354
  details["actual_amount"] = amount_int
344
355
  except (ValueError, TypeError):
345
356
  return {
@@ -366,7 +377,6 @@ class ActionExecutor:
366
377
 
367
378
  # Press Enter on whatever element currently has focus
368
379
  await self.page.keyboard.press("Enter")
369
- await asyncio.sleep(0.3)
370
380
  return {
371
381
  "message": "Pressed Enter on focused element",
372
382
  "details": details,
@@ -378,13 +388,13 @@ class ActionExecutor:
378
388
  try:
379
389
  # Wait for basic DOM content loading
380
390
  await self.page.wait_for_load_state(
381
- 'domcontentloaded', timeout=self.SHORT_TIMEOUT
391
+ 'domcontentloaded', timeout=self.short_timeout
382
392
  )
383
393
 
384
394
  # Try to wait for network idle briefly
385
395
  try:
386
396
  await self.page.wait_for_load_state(
387
- 'networkidle', timeout=self.SHORT_TIMEOUT
397
+ 'networkidle', timeout=self.short_timeout
388
398
  )
389
399
  except Exception:
390
400
  pass # Network idle is optional
@@ -81,9 +81,15 @@ what was accomplished
81
81
  headless: bool = False,
82
82
  stealth: bool = False,
83
83
  model_backend: Optional[BaseModelBackend] = None,
84
+ default_timeout: Optional[int] = None,
85
+ short_timeout: Optional[int] = None,
84
86
  ):
85
87
  self._session = HybridBrowserSession(
86
- headless=headless, user_data_dir=user_data_dir, stealth=stealth
88
+ headless=headless,
89
+ user_data_dir=user_data_dir,
90
+ stealth=stealth,
91
+ default_timeout=default_timeout,
92
+ short_timeout=short_timeout,
87
93
  )
88
94
  from camel.agents import ChatAgent
89
95
 
@@ -19,8 +19,8 @@ from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Tuple
19
19
  from camel.logger import get_logger
20
20
 
21
21
  from .actions import ActionExecutor
22
+ from .config_loader import ConfigLoader
22
23
  from .snapshot import PageSnapshot
23
- from .stealth_config import StealthConfig
24
24
 
25
25
  if TYPE_CHECKING:
26
26
  from playwright.async_api import (
@@ -44,10 +44,6 @@ class HybridBrowserSession:
44
44
  This class is a singleton per event-loop and session-id combination.
45
45
  """
46
46
 
47
- # Configuration constants
48
- DEFAULT_NAVIGATION_TIMEOUT = 10000 # 10 seconds
49
- NETWORK_IDLE_TIMEOUT = 5000 # 5 seconds
50
-
51
47
  # Class-level registry for singleton instances
52
48
  # Format: {(loop_id, session_id): HybridBrowserSession}
53
49
  _instances: ClassVar[Dict[Tuple[Any, str], "HybridBrowserSession"]] = {}
@@ -63,6 +59,10 @@ class HybridBrowserSession:
63
59
  user_data_dir: Optional[str] = None,
64
60
  stealth: bool = False,
65
61
  session_id: Optional[str] = None,
62
+ default_timeout: Optional[int] = None,
63
+ short_timeout: Optional[int] = None,
64
+ navigation_timeout: Optional[int] = None,
65
+ network_idle_timeout: Optional[int] = None,
66
66
  ) -> "HybridBrowserSession":
67
67
  # Create a unique key for this event loop and session combination
68
68
  # We defer the event loop lookup to avoid issues with creation
@@ -75,6 +75,10 @@ class HybridBrowserSession:
75
75
  "user_data_dir": user_data_dir,
76
76
  "stealth": stealth,
77
77
  "session_id": session_id,
78
+ "default_timeout": default_timeout,
79
+ "short_timeout": short_timeout,
80
+ "navigation_timeout": navigation_timeout,
81
+ "network_idle_timeout": network_idle_timeout,
78
82
  }
79
83
  return instance
80
84
 
@@ -126,6 +130,10 @@ class HybridBrowserSession:
126
130
  user_data_dir: Optional[str] = None,
127
131
  stealth: bool = False,
128
132
  session_id: Optional[str] = None,
133
+ default_timeout: Optional[int] = None,
134
+ short_timeout: Optional[int] = None,
135
+ navigation_timeout: Optional[int] = None,
136
+ network_idle_timeout: Optional[int] = None,
129
137
  ):
130
138
  if self._initialized:
131
139
  return
@@ -136,12 +144,27 @@ class HybridBrowserSession:
136
144
  self._stealth = stealth
137
145
  self._session_id = session_id or "default"
138
146
 
147
+ # Store timeout configuration for ActionExecutor instances and
148
+ # browser operations
149
+ self._default_timeout = default_timeout
150
+ self._short_timeout = short_timeout
151
+ self._navigation_timeout = ConfigLoader.get_navigation_timeout(
152
+ navigation_timeout
153
+ )
154
+ self._network_idle_timeout = ConfigLoader.get_network_idle_timeout(
155
+ network_idle_timeout
156
+ )
157
+
139
158
  # Initialize _creation_params to fix linter error
140
159
  self._creation_params = {
141
160
  "headless": headless,
142
161
  "user_data_dir": user_data_dir,
143
162
  "stealth": stealth,
144
163
  "session_id": session_id,
164
+ "default_timeout": default_timeout,
165
+ "short_timeout": short_timeout,
166
+ "navigation_timeout": navigation_timeout,
167
+ "network_idle_timeout": network_idle_timeout,
145
168
  }
146
169
 
147
170
  self._playwright: Optional[Playwright] = None
@@ -164,7 +187,8 @@ class HybridBrowserSession:
164
187
  self._stealth_config: Optional[Dict[str, Any]] = None
165
188
  if self._stealth:
166
189
  self._stealth_script = self._load_stealth_script()
167
- self._stealth_config = StealthConfig.get_all_config()
190
+ stealth_config_class = ConfigLoader.get_stealth_config()
191
+ self._stealth_config = stealth_config_class.get_stealth_config()
168
192
 
169
193
  def _load_stealth_script(self) -> str:
170
194
  r"""Load the stealth JavaScript script from file."""
@@ -231,9 +255,7 @@ class HybridBrowserSession:
231
255
  # Navigate if URL provided
232
256
  if url:
233
257
  try:
234
- await new_page.goto(
235
- url, timeout=self.DEFAULT_NAVIGATION_TIMEOUT
236
- )
258
+ await new_page.goto(url, timeout=self._navigation_timeout)
237
259
  await new_page.wait_for_load_state('domcontentloaded')
238
260
  except Exception as e:
239
261
  logger.warning(f"Failed to navigate new tab to {url}: {e}")
@@ -319,7 +341,12 @@ class HybridBrowserSession:
319
341
  await self._page.bring_to_front()
320
342
 
321
343
  # Update executor and snapshot for new tab
322
- self.executor = ActionExecutor(self._page, self)
344
+ self.executor = ActionExecutor(
345
+ self._page,
346
+ self,
347
+ default_timeout=self._default_timeout,
348
+ short_timeout=self._short_timeout,
349
+ )
323
350
  self.snapshot = PageSnapshot(self._page)
324
351
 
325
352
  logger.info(f"Switched to tab {tab_index}")
@@ -506,14 +533,17 @@ class HybridBrowserSession:
506
533
  logger.warning(f"Failed to apply stealth script: {e}")
507
534
 
508
535
  # Set up timeout for navigation
509
- self._page.set_default_navigation_timeout(
510
- self.DEFAULT_NAVIGATION_TIMEOUT
511
- )
512
- self._page.set_default_timeout(self.DEFAULT_NAVIGATION_TIMEOUT)
536
+ self._page.set_default_navigation_timeout(self._navigation_timeout)
537
+ self._page.set_default_timeout(self._navigation_timeout)
513
538
 
514
539
  # Initialize utilities
515
540
  self.snapshot = PageSnapshot(self._page)
516
- self.executor = ActionExecutor(self._page, self)
541
+ self.executor = ActionExecutor(
542
+ self._page,
543
+ self,
544
+ default_timeout=self._default_timeout,
545
+ short_timeout=self._short_timeout,
546
+ )
517
547
  self._current_tab_index = 0
518
548
 
519
549
  logger.info("Browser session initialized successfully")
@@ -579,7 +609,7 @@ class HybridBrowserSession:
579
609
  await page.close()
580
610
  logger.debug(
581
611
  f"Closed page: "
582
- f"{page.url if hasattr(page, 'url') else 'unknown'}" # noqa: E501
612
+ f"{page.url if hasattr(page, 'url') else 'unknown'}" # noqa:E501
583
613
  )
584
614
  except Exception as e:
585
615
  logger.warning(f"Error closing page: {e}")
@@ -668,13 +698,13 @@ class HybridBrowserSession:
668
698
  await self.ensure_browser()
669
699
  page = await self.get_page()
670
700
 
671
- await page.goto(url, timeout=self.DEFAULT_NAVIGATION_TIMEOUT)
701
+ await page.goto(url, timeout=self._navigation_timeout)
672
702
  await page.wait_for_load_state('domcontentloaded')
673
703
 
674
704
  # Try to wait for network idle
675
705
  try:
676
706
  await page.wait_for_load_state(
677
- 'networkidle', timeout=self.NETWORK_IDLE_TIMEOUT
707
+ 'networkidle', timeout=self._network_idle_timeout
678
708
  )
679
709
  except Exception:
680
710
  logger.debug("Network idle timeout - continuing anyway")