camel-ai 0.2.72a10__py3-none-any.whl → 0.2.73a1__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.

Files changed (37) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +113 -338
  3. camel/memories/agent_memories.py +18 -17
  4. camel/societies/workforce/prompts.py +10 -4
  5. camel/societies/workforce/single_agent_worker.py +7 -5
  6. camel/toolkits/__init__.py +6 -1
  7. camel/toolkits/base.py +57 -1
  8. camel/toolkits/hybrid_browser_toolkit/config_loader.py +136 -413
  9. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +796 -1631
  10. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4356 -0
  11. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  12. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  13. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +945 -0
  14. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +226 -0
  15. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +522 -0
  16. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  17. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +110 -0
  18. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  19. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +210 -0
  20. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +533 -0
  21. camel/toolkits/message_integration.py +592 -0
  22. camel/toolkits/notion_mcp_toolkit.py +234 -0
  23. camel/toolkits/screenshot_toolkit.py +116 -31
  24. camel/toolkits/search_toolkit.py +20 -2
  25. camel/toolkits/terminal_toolkit.py +16 -2
  26. camel/toolkits/video_analysis_toolkit.py +13 -13
  27. camel/toolkits/video_download_toolkit.py +11 -11
  28. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a1.dist-info}/METADATA +12 -6
  29. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a1.dist-info}/RECORD +31 -24
  30. camel/toolkits/hybrid_browser_toolkit/actions.py +0 -417
  31. camel/toolkits/hybrid_browser_toolkit/agent.py +0 -311
  32. camel/toolkits/hybrid_browser_toolkit/browser_session.py +0 -740
  33. camel/toolkits/hybrid_browser_toolkit/snapshot.py +0 -227
  34. camel/toolkits/hybrid_browser_toolkit/stealth_script.js +0 -0
  35. camel/toolkits/hybrid_browser_toolkit/unified_analyzer.js +0 -1002
  36. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a1.dist-info}/WHEEL +0 -0
  37. {camel_ai-0.2.72a10.dist-info → camel_ai-0.2.73a1.dist-info}/licenses/LICENSE +0 -0
@@ -1,227 +0,0 @@
1
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
2
- # Licensed under the Apache License, Version 2.0 (the "License");
3
- # you may not use this file except in compliance with the License.
4
- # You may obtain a copy of the License at
5
- #
6
- # http://www.apache.org/licenses/LICENSE-2.0
7
- #
8
- # Unless required by applicable law or agreed to in writing, software
9
- # distributed under the License is distributed on an "AS IS" BASIS,
10
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
- # See the License for the specific language governing permissions and
12
- # limitations under the License.
13
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
- from pathlib import Path
15
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
16
-
17
- if TYPE_CHECKING:
18
- from playwright.async_api import Page
19
-
20
- # Logging support
21
- from camel.logger import get_logger
22
-
23
- from .config_loader import ConfigLoader
24
-
25
- logger = get_logger(__name__)
26
-
27
-
28
- class PageSnapshot:
29
- """Utility for capturing YAML-like page snapshots and diff-only
30
- variants."""
31
-
32
- def __init__(self, page: "Page"):
33
- self.page = page
34
- self.snapshot_data: Optional[str] = None # last full snapshot
35
- self._last_url: Optional[str] = None
36
- self.last_info: Dict[str, List[int] | bool] = {
37
- "is_diff": False,
38
- "priorities": [1, 2, 3],
39
- }
40
- self.dom_timeout = ConfigLoader.get_dom_content_loaded_timeout()
41
-
42
- # ---------------------------------------------------------------------
43
- # Public API
44
- # ---------------------------------------------------------------------
45
- async def capture(
46
- self, *, force_refresh: bool = False, diff_only: bool = False
47
- ) -> str:
48
- """Return current snapshot or just the diff to previous one."""
49
- try:
50
- current_url = self.page.url
51
-
52
- # Previously we skipped regeneration when the URL had not changed
53
- # and no explicit refresh was requested. This prevented the agent
54
- # from seeing DOM updates that occur without a navigation (e.g.
55
- # single-page apps, dynamic games such as Wordle). The early-exit
56
- # logic has been removed so that we always capture a *fresh* DOM
57
- # snapshot. If the snapshot happens to be byte-for-byte identical
58
- # to the previous one we simply return it after the standard
59
- # comparison step below; otherwise callers receive the updated
60
- # snapshot even when the URL did not change.
61
-
62
- # ensure DOM stability
63
- await self.page.wait_for_load_state(
64
- 'domcontentloaded', timeout=self.dom_timeout
65
- )
66
-
67
- logger.debug("Capturing page snapshot …")
68
- snapshot_result = await self._get_snapshot_direct()
69
-
70
- # Extract snapshot text from the unified analyzer result
71
- if (
72
- isinstance(snapshot_result, dict)
73
- and 'snapshotText' in snapshot_result
74
- ):
75
- snapshot_text = snapshot_result['snapshotText']
76
- else:
77
- snapshot_text = snapshot_result
78
-
79
- formatted = self._format_snapshot(snapshot_text or "<empty>")
80
-
81
- output = formatted
82
- if diff_only and self.snapshot_data:
83
- output = self._compute_diff(self.snapshot_data, formatted)
84
-
85
- # update cache with *full* snapshot (not diff)
86
- self._last_url = current_url
87
- self.snapshot_data = formatted
88
-
89
- # analyse priorities present (only for non-diff)
90
- priorities_included = self._detect_priorities(
91
- formatted if not diff_only else self.snapshot_data or formatted
92
- )
93
- self.last_info = {
94
- "is_diff": diff_only and self.snapshot_data is not None,
95
- "priorities": priorities_included,
96
- }
97
-
98
- logger.debug(
99
- "Snapshot captured. Diff_only=%s, priorities=%s",
100
- diff_only,
101
- self.last_info["priorities"],
102
- )
103
- return output
104
- except Exception as exc:
105
- logger.error("Snapshot capture failed: %s", exc)
106
- return f"Error: Could not capture page snapshot {exc}"
107
-
108
- # ------------------------------------------------------------------
109
- # Internal helpers
110
- # ------------------------------------------------------------------
111
- _snapshot_js_cache: Optional[str] = None # class-level cache
112
-
113
- async def _get_snapshot_direct(
114
- self,
115
- ) -> Optional[Union[str, Dict[str, Any]]]:
116
- r"""Evaluate the snapshot-extraction JS with simple retry logic.
117
-
118
- Playwright throws *Execution context was destroyed* when a new page
119
- navigation happens between scheduling and evaluating the JS. In that
120
- case we retry a few times after waiting for the next DOMContentLoaded
121
- event; for all other exceptions we abort immediately.
122
- """
123
-
124
- # Load JS once and cache it at class level
125
- if PageSnapshot._snapshot_js_cache is None:
126
- js_path = Path(__file__).parent / "unified_analyzer.js"
127
- PageSnapshot._snapshot_js_cache = js_path.read_text(
128
- encoding="utf-8"
129
- )
130
-
131
- js_code = PageSnapshot._snapshot_js_cache
132
-
133
- retries: int = 3
134
- while retries > 0:
135
- try:
136
- return await self.page.evaluate(js_code)
137
- except Exception as e:
138
- msg = str(e)
139
-
140
- # Typical error when navigation happens between calls
141
- nav_err = "Execution context was destroyed"
142
-
143
- if (
144
- nav_err in msg
145
- or "Most likely because of a navigation" in msg
146
- ):
147
- retries -= 1
148
- logger.debug(
149
- "Snapshot evaluate failed due to navigation; "
150
- "retrying (%d left)…",
151
- retries,
152
- )
153
-
154
- # Wait for next DOM stability before retrying
155
- try:
156
- await self.page.wait_for_load_state(
157
- "domcontentloaded", timeout=self.dom_timeout
158
- )
159
- except Exception:
160
- # Even if waiting fails, attempt retry to give it
161
- # one more chance
162
- pass
163
-
164
- continue # retry the evaluate()
165
-
166
- # Any other exception → abort
167
- logger.warning(
168
- "Failed to execute snapshot JavaScript: %s",
169
- e,
170
- )
171
- return None
172
-
173
- logger.warning("Failed to execute snapshot JavaScript after retries")
174
- return None
175
-
176
- @staticmethod
177
- def _format_snapshot(text: str) -> str:
178
- return "\n".join(["- Page Snapshot", "```yaml", text, "```"])
179
-
180
- @staticmethod
181
- def _compute_diff(old: str, new: str) -> str:
182
- if not old or not new:
183
- return "- Page Snapshot (error: missing data for diff)"
184
-
185
- import difflib
186
-
187
- diff = list(
188
- difflib.unified_diff(
189
- old.splitlines(False),
190
- new.splitlines(False),
191
- fromfile='prev',
192
- tofile='curr',
193
- lineterm='',
194
- )
195
- )
196
- if not diff:
197
- return "- Page Snapshot (no structural changes)"
198
- return "\n".join(["- Page Snapshot (diff)", "```diff", *diff, "```"])
199
-
200
- # ------------------------------------------------------------------
201
- def _detect_priorities(self, snapshot_yaml: str) -> List[int]:
202
- """Return sorted list of priorities present (1,2,3)."""
203
- priorities = set()
204
- for line in snapshot_yaml.splitlines():
205
- if '[ref=' not in line:
206
- continue
207
- lower_line = line.lower()
208
- if any(
209
- r in lower_line
210
- for r in (
211
- "input",
212
- "button",
213
- "select",
214
- "textarea",
215
- "checkbox",
216
- "radio",
217
- "link",
218
- )
219
- ):
220
- priorities.add(1)
221
- elif 'label' in lower_line:
222
- priorities.add(2)
223
- else:
224
- priorities.add(3)
225
- if not priorities:
226
- priorities.add(3)
227
- return sorted(priorities)