camel-ai 0.2.71a2__py3-none-any.whl → 0.2.71a4__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 (32) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/chat_agent.py +297 -16
  4. camel/interpreters/docker_interpreter.py +3 -2
  5. camel/loaders/base_loader.py +85 -0
  6. camel/messages/base.py +2 -6
  7. camel/services/agent_openapi_server.py +380 -0
  8. camel/societies/workforce/workforce.py +144 -33
  9. camel/toolkits/__init__.py +7 -4
  10. camel/toolkits/craw4ai_toolkit.py +2 -2
  11. camel/toolkits/file_write_toolkit.py +6 -6
  12. camel/toolkits/{non_visual_browser_toolkit → hybrid_browser_toolkit}/__init__.py +2 -2
  13. camel/toolkits/{non_visual_browser_toolkit → hybrid_browser_toolkit}/actions.py +47 -11
  14. camel/toolkits/{non_visual_browser_toolkit → hybrid_browser_toolkit}/agent.py +21 -11
  15. camel/toolkits/{non_visual_browser_toolkit/nv_browser_session.py → hybrid_browser_toolkit/browser_session.py} +64 -10
  16. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +1008 -0
  17. camel/toolkits/{non_visual_browser_toolkit → hybrid_browser_toolkit}/snapshot.py +16 -4
  18. camel/toolkits/{non_visual_browser_toolkit/snapshot.js → hybrid_browser_toolkit/unified_analyzer.js} +202 -23
  19. camel/toolkits/note_taking_toolkit.py +90 -0
  20. camel/toolkits/openai_image_toolkit.py +292 -0
  21. camel/toolkits/slack_toolkit.py +4 -4
  22. camel/toolkits/terminal_toolkit.py +223 -73
  23. camel/types/agents/tool_calling_record.py +4 -1
  24. camel/types/enums.py +24 -24
  25. camel/utils/mcp_client.py +37 -1
  26. camel/utils/tool_result.py +44 -0
  27. {camel_ai-0.2.71a2.dist-info → camel_ai-0.2.71a4.dist-info}/METADATA +58 -5
  28. {camel_ai-0.2.71a2.dist-info → camel_ai-0.2.71a4.dist-info}/RECORD +30 -26
  29. camel/toolkits/dalle_toolkit.py +0 -175
  30. camel/toolkits/non_visual_browser_toolkit/browser_non_visual_toolkit.py +0 -446
  31. {camel_ai-0.2.71a2.dist-info → camel_ai-0.2.71a4.dist-info}/WHEEL +0 -0
  32. {camel_ai-0.2.71a2.dist-info → camel_ai-0.2.71a4.dist-info}/licenses/LICENSE +0 -0
@@ -12,7 +12,7 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from pathlib import Path
15
- from typing import TYPE_CHECKING, Dict, List, Optional
15
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
16
16
 
17
17
  if TYPE_CHECKING:
18
18
  from playwright.async_api import Page
@@ -64,7 +64,17 @@ class PageSnapshot:
64
64
  )
65
65
 
66
66
  logger.debug("Capturing page snapshot …")
67
- snapshot_text = await self._get_snapshot_direct()
67
+ snapshot_result = await self._get_snapshot_direct()
68
+
69
+ # Extract snapshot text from the unified analyzer result
70
+ if (
71
+ isinstance(snapshot_result, dict)
72
+ and 'snapshotText' in snapshot_result
73
+ ):
74
+ snapshot_text = snapshot_result['snapshotText']
75
+ else:
76
+ snapshot_text = snapshot_result
77
+
68
78
  formatted = self._format_snapshot(snapshot_text or "<empty>")
69
79
 
70
80
  output = formatted
@@ -99,7 +109,9 @@ class PageSnapshot:
99
109
  # ------------------------------------------------------------------
100
110
  _snapshot_js_cache: Optional[str] = None # class-level cache
101
111
 
102
- async def _get_snapshot_direct(self) -> Optional[str]:
112
+ async def _get_snapshot_direct(
113
+ self,
114
+ ) -> Optional[Union[str, Dict[str, Any]]]:
103
115
  r"""Evaluate the snapshot-extraction JS with simple retry logic.
104
116
 
105
117
  Playwright throws *Execution context was destroyed* when a new page
@@ -110,7 +122,7 @@ class PageSnapshot:
110
122
 
111
123
  # Load JS once and cache it at class level
112
124
  if PageSnapshot._snapshot_js_cache is None:
113
- js_path = Path(__file__).parent / "snapshot.js"
125
+ js_path = Path(__file__).parent / "unified_analyzer.js"
114
126
  PageSnapshot._snapshot_js_cache = js_path.read_text(
115
127
  encoding="utf-8"
116
128
  )
@@ -1,20 +1,40 @@
1
1
  (() => {
2
- // Playwright's snapshot logic focuses on semantics and visibility, not arbitrary limits.
3
- // We will first build a semantic tree in memory, then render it.
2
+ // Unified analyzer that combines visual and structural analysis
3
+ // Preserves complete snapshot.js logic while adding visual coordinate information
4
+
5
+ let refCounter = 1;
6
+ function generateRef() {
7
+ return `e${refCounter++}`;
8
+ }
9
+
10
+ // === Complete snapshot.js logic preservation ===
4
11
 
5
12
  function isVisible(node) {
13
+ // Check if node is null or not a valid DOM node
14
+ if (!node || typeof node.nodeType === 'undefined') return false;
6
15
  if (node.nodeType !== Node.ELEMENT_NODE) return true;
7
- const style = window.getComputedStyle(node);
8
- if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0')
16
+
17
+ try {
18
+ const style = window.getComputedStyle(node);
19
+ if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0')
20
+ return false;
21
+ // An element with `display: contents` is not rendered itself, but its children are.
22
+ if (style.display === 'contents')
23
+ return true;
24
+ const rect = node.getBoundingClientRect();
25
+ return rect.width > 0 && rect.height > 0;
26
+ } catch (e) {
27
+ // If there's an error getting computed style or bounding rect, assume element is not visible
9
28
  return false;
10
- // An element with `display: contents` is not rendered itself, but its children are.
11
- if (style.display === 'contents')
12
- return true;
13
- const rect = node.getBoundingClientRect();
14
- return rect.width > 0 && rect.height > 0;
29
+ }
15
30
  }
16
31
 
17
32
  function getRole(node) {
33
+ // Check if node is null or doesn't have required properties
34
+ if (!node || !node.tagName || !node.getAttribute) {
35
+ return 'generic';
36
+ }
37
+
18
38
  const role = node.getAttribute('role');
19
39
  if (role) return role;
20
40
 
@@ -32,6 +52,9 @@
32
52
  }
33
53
 
34
54
  function getAccessibleName(node) {
55
+ // Check if node is null or doesn't have required methods
56
+ if (!node || !node.hasAttribute || !node.getAttribute) return '';
57
+
35
58
  if (node.hasAttribute('aria-label')) return node.getAttribute('aria-label') || '';
36
59
  if (node.hasAttribute('aria-labelledby')) {
37
60
  const id = node.getAttribute('aria-labelledby');
@@ -48,6 +71,9 @@
48
71
 
49
72
  const textCache = new Map();
50
73
  function getVisibleTextContent(_node) {
74
+ // Check if node is null or doesn't have nodeType
75
+ if (!_node || typeof _node.nodeType === 'undefined') return '';
76
+
51
77
  if (textCache.has(_node)) return textCache.get(_node);
52
78
 
53
79
  if (_node.nodeType === Node.TEXT_NODE) {
@@ -70,18 +96,17 @@
70
96
  return result;
71
97
  }
72
98
 
73
- let refCounter = 1;
74
- function generateRef() {
75
- return `e${refCounter++}`;
76
- }
77
-
78
99
  /**
79
100
  * Phase 1: Build an in-memory representation of the accessibility tree.
101
+ * Complete preservation of snapshot.js buildAriaTree logic
80
102
  */
81
103
  function buildAriaTree(rootElement) {
82
104
  const visited = new Set();
83
105
 
84
106
  function toAriaNode(element) {
107
+ // Check if element is null or not a valid DOM element
108
+ if (!element || !element.tagName) return null;
109
+
85
110
  // Only consider visible elements
86
111
  if (!isVisible(element)) return null;
87
112
 
@@ -112,7 +137,8 @@
112
137
  }
113
138
 
114
139
  function traverse(element, parentNode) {
115
- if (visited.has(element)) return;
140
+ // Check if element is null or not a valid DOM element
141
+ if (!element || !element.tagName || visited.has(element)) return;
116
142
  visited.add(element);
117
143
 
118
144
  // FIX: Completely skip script and style tags and their children.
@@ -153,9 +179,34 @@
153
179
  }
154
180
  }
155
181
 
156
- // FIX: If an element's name is the same as its only text child, remove the redundant child.
157
- if (ariaNode && ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string' && ariaNode.name === ariaNode.children[0]) {
158
- ariaNode.children = [];
182
+ // FIX: Remove redundant text children that match the element's name
183
+ if (ariaNode && ariaNode.children.length > 0) {
184
+ // Remove text children that are the same as the parent's name or are contained in it
185
+ ariaNode.children = ariaNode.children.filter(child => {
186
+ if (typeof child === 'string') {
187
+ const childText = child.trim();
188
+ const parentName = ariaNode.name.trim();
189
+
190
+ // Remove if text child exactly matches parent name
191
+ if (childText === parentName) {
192
+ return false;
193
+ }
194
+
195
+ // Also remove if the child text is completely contained in parent name
196
+ // and represents a significant portion (to avoid removing important partial text)
197
+ if (childText.length > 3 && parentName.includes(childText)) {
198
+ return false;
199
+ }
200
+
201
+ return true;
202
+ }
203
+ return true;
204
+ });
205
+
206
+ // If after filtering, we have only one text child that equals the name, remove it
207
+ if (ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string' && ariaNode.name === ariaNode.children[0]) {
208
+ ariaNode.children = [];
209
+ }
159
210
  }
160
211
  }
161
212
 
@@ -166,7 +217,7 @@
166
217
 
167
218
  /**
168
219
  * Phase 2: Normalize the tree by removing redundant generic wrappers.
169
- * This is a key optimization in Playwright to simplify the structure.
220
+ * Complete preservation of snapshot.js normalizeTree logic
170
221
  */
171
222
  function normalizeTree(node) {
172
223
  if (typeof node === 'string') return [node];
@@ -178,6 +229,24 @@
178
229
  node.children = newChildren;
179
230
 
180
231
  // Remove child elements that have the same name as their parent
232
+ const filteredChildren = [];
233
+ for (const child of node.children) {
234
+ if (typeof child !== 'string' && child.name && node.name) {
235
+ const childName = child.name.trim();
236
+ const parentName = node.name.trim();
237
+ if (childName === parentName) {
238
+ // If child has same name as parent, merge its children into parent
239
+ filteredChildren.push(...(child.children || []));
240
+ } else {
241
+ filteredChildren.push(child);
242
+ }
243
+ } else {
244
+ filteredChildren.push(child);
245
+ }
246
+ }
247
+ node.children = filteredChildren;
248
+
249
+ // Also handle the case where we have only one child with same name
181
250
  if (node.children.length === 1 && typeof node.children[0] !== 'string') {
182
251
  const child = node.children[0];
183
252
  if (child.name && node.name && child.name.trim() === node.name.trim()) {
@@ -195,9 +264,9 @@
195
264
  return [node];
196
265
  }
197
266
 
198
-
199
267
  /**
200
268
  * Phase 3: Render the normalized tree into the final string format.
269
+ * Complete preservation of snapshot.js renderTree logic
201
270
  */
202
271
  function renderTree(node, indent = '') {
203
272
  const lines = [];
@@ -263,6 +332,116 @@
263
332
  return lines;
264
333
  }
265
334
 
266
- const outputLines = processDocument(document);
267
- return outputLines.join('\n');
268
- })();
335
+ // === Visual analysis functions from page_script.js ===
336
+
337
+ // From page_script.js - check if element is topmost at coordinates
338
+ function isTopmost(element, x, y) {
339
+ let hit = document.elementFromPoint(x, y);
340
+ if (hit === null) return true;
341
+
342
+ while (hit) {
343
+ if (hit == element) return true;
344
+ hit = hit.parentNode;
345
+ }
346
+ return false;
347
+ }
348
+
349
+ // From page_script.js - get visual coordinates
350
+ function getElementCoordinates(element) {
351
+ let rects = element.getClientRects();
352
+ let scale = window.devicePixelRatio || 1;
353
+ let validRects = [];
354
+
355
+ for (const rect of rects) {
356
+ let x = rect.left + rect.width / 2;
357
+ let y = rect.top + rect.height / 2;
358
+ if (isTopmost(element, x, y)) {
359
+ validRects.push({
360
+ x: rect.x * scale,
361
+ y: rect.y * scale,
362
+ width: rect.width * scale,
363
+ height: rect.height * scale,
364
+ top: rect.top * scale,
365
+ left: rect.left * scale,
366
+ right: rect.right * scale,
367
+ bottom: rect.bottom * scale
368
+ });
369
+ }
370
+ }
371
+
372
+ return validRects;
373
+ }
374
+
375
+ // === Unified analysis function ===
376
+
377
+ function collectElementsFromTree(node, elementsMap) {
378
+ if (typeof node === 'string') return;
379
+
380
+ if (node.element && node.ref) {
381
+ // Get visual coordinates for this element
382
+ const coordinates = getElementCoordinates(node.element);
383
+
384
+ // Store comprehensive element information
385
+ elementsMap[node.ref] = {
386
+ // Structural information (preserved from snapshot.js)
387
+ role: node.role,
388
+ name: node.name,
389
+ tagName: node.element.tagName.toLowerCase(),
390
+ disabled: node.disabled,
391
+ checked: node.checked,
392
+ expanded: node.expanded,
393
+
394
+ // Visual information (from page_script.js)
395
+ coordinates: coordinates,
396
+
397
+ // Additional metadata
398
+ href: node.element.href || null,
399
+ value: node.element.value || null,
400
+ placeholder: node.element.placeholder || null,
401
+ scrollable: node.element.scrollHeight > node.element.clientHeight
402
+ };
403
+ }
404
+
405
+ // Recursively process children
406
+ if (node.children) {
407
+ for (const child of node.children) {
408
+ collectElementsFromTree(child, elementsMap);
409
+ }
410
+ }
411
+ }
412
+
413
+ function analyzePageElements() {
414
+ // Generate the complete structured snapshot using original snapshot.js logic
415
+ const outputLines = processDocument(document);
416
+ const snapshotText = outputLines.join('\n');
417
+
418
+ // Build the tree again to collect element information with visual data
419
+ textCache.clear();
420
+ refCounter = 1; // Reset counter to match snapshot generation
421
+ let tree = buildAriaTree(document.body);
422
+ [tree] = normalizeTree(tree);
423
+
424
+ const elementsMap = {};
425
+ collectElementsFromTree(tree, elementsMap);
426
+
427
+ const result = {
428
+ url: window.location.href,
429
+ elements: elementsMap,
430
+ snapshotText: snapshotText,
431
+ metadata: {
432
+ timestamp: new Date().toISOString(),
433
+ elementCount: Object.keys(elementsMap).length,
434
+ screenInfo: {
435
+ width: window.innerWidth,
436
+ height: window.innerHeight,
437
+ devicePixelRatio: window.devicePixelRatio || 1
438
+ }
439
+ }
440
+ };
441
+
442
+ return result;
443
+ }
444
+
445
+ // Execute analysis and return result
446
+ return analyzePageElements();
447
+ })();
@@ -0,0 +1,90 @@
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 List, Optional
16
+
17
+ from camel.toolkits.base import BaseToolkit
18
+ from camel.toolkits.function_tool import FunctionTool
19
+
20
+
21
+ class NoteTakingToolkit(BaseToolkit):
22
+ r"""A toolkit for taking notes in a Markdown file.
23
+
24
+ This toolkit allows an agent to create, append to, and update a specific
25
+ Markdown file for note-taking purposes.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ note_file_path: str = "notes/notes.md",
31
+ timeout: Optional[float] = None,
32
+ ) -> None:
33
+ r"""Initialize the NoteTakingToolkit.
34
+
35
+ Args:
36
+ note_file_path (str): The path to the note file.
37
+ (default: :obj:`notes/notes.md`)
38
+ timeout (Optional[float]): The timeout for the toolkit.
39
+ """
40
+ super().__init__(timeout=timeout)
41
+ self.note_file_path = Path(note_file_path)
42
+ self.note_file_path.parent.mkdir(parents=True, exist_ok=True)
43
+
44
+ def take_note(self, content: str, update: bool = False) -> str:
45
+ r"""Takes a note and saves it to the note file.
46
+
47
+ Args:
48
+ content (str): The content of the note to be saved.
49
+ update (bool): If True, the existing note file will be
50
+ overwritten with the new content. If False, the new content
51
+ will be appended to the end of the file.
52
+ (default: :obj:`False`)
53
+
54
+ Returns:
55
+ str: A message indicating the result of the operation.
56
+ """
57
+ mode = "w" if update else "a"
58
+ try:
59
+ with self.note_file_path.open(mode, encoding="utf-8") as f:
60
+ f.write(content + "\n")
61
+ action = "updated" if update else "appended to"
62
+ return f"Note successfully {action} in {self.note_file_path}."
63
+ except Exception as e:
64
+ return f"Error taking note: {e}"
65
+
66
+ def read_note(self) -> str:
67
+ r"""Reads the content of the note file.
68
+
69
+ Returns:
70
+ str: The content of the note file, or an error message if the
71
+ file cannot be read.
72
+ """
73
+ try:
74
+ if not self.note_file_path.exists():
75
+ return "Note file does not exist yet."
76
+ return self.note_file_path.read_text(encoding="utf-8")
77
+ except Exception as e:
78
+ return f"Error reading note: {e}"
79
+
80
+ def get_tools(self) -> List[FunctionTool]:
81
+ r"""Return a list of FunctionTool objects representing the functions
82
+ in the toolkit.
83
+
84
+ Returns:
85
+ List[FunctionTool]: A list of FunctionTool objects.
86
+ """
87
+ return [
88
+ FunctionTool(self.take_note),
89
+ FunctionTool(self.read_note),
90
+ ]