camel-ai 0.2.75a5__py3-none-any.whl → 0.2.76a0__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 (47) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +298 -130
  3. camel/configs/__init__.py +6 -0
  4. camel/configs/amd_config.py +70 -0
  5. camel/configs/nebius_config.py +103 -0
  6. camel/interpreters/__init__.py +2 -0
  7. camel/interpreters/microsandbox_interpreter.py +395 -0
  8. camel/models/__init__.py +4 -0
  9. camel/models/amd_model.py +101 -0
  10. camel/models/model_factory.py +4 -0
  11. camel/models/nebius_model.py +83 -0
  12. camel/models/ollama_model.py +3 -3
  13. camel/models/openai_model.py +0 -6
  14. camel/runtimes/daytona_runtime.py +11 -12
  15. camel/societies/workforce/task_channel.py +120 -27
  16. camel/societies/workforce/workforce.py +35 -3
  17. camel/toolkits/__init__.py +5 -3
  18. camel/toolkits/code_execution.py +28 -1
  19. camel/toolkits/function_tool.py +6 -1
  20. camel/toolkits/github_toolkit.py +104 -17
  21. camel/toolkits/hybrid_browser_toolkit/config_loader.py +8 -0
  22. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +12 -0
  23. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +33 -14
  24. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +135 -40
  25. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +2 -0
  26. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +43 -207
  27. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  28. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +231 -0
  29. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  30. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
  31. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +248 -58
  32. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +5 -1
  33. camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
  34. camel/toolkits/math_toolkit.py +64 -10
  35. camel/toolkits/mcp_toolkit.py +39 -14
  36. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  37. camel/toolkits/search_toolkit.py +13 -2
  38. camel/toolkits/terminal_toolkit.py +12 -2
  39. camel/toolkits/video_analysis_toolkit.py +16 -10
  40. camel/types/enums.py +42 -0
  41. camel/types/unified_model_type.py +5 -0
  42. camel/utils/commons.py +2 -0
  43. camel/utils/mcp.py +136 -2
  44. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/METADATA +5 -11
  45. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/RECORD +47 -38
  46. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/WHEEL +0 -0
  47. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Parent-child filtering logic for SOM-labels
3
+ * Filters out child elements that are contained within propagating parent elements
4
+ */
5
+
6
+ export interface ElementInfo {
7
+ ref: string;
8
+ coordinates?: {
9
+ x: number;
10
+ y: number;
11
+ width: number;
12
+ height: number;
13
+ };
14
+ role?: string;
15
+ type?: string;
16
+ tagName?: string;
17
+ attributes?: Record<string, any>;
18
+ text?: string;
19
+ }
20
+
21
+ // Elements that propagate bounds to their children
22
+ const PROPAGATING_ELEMENTS = [
23
+ { tag: 'a', role: null },
24
+ { tag: 'button', role: null },
25
+ { tag: 'div', role: 'button' },
26
+ { tag: 'div', role: 'combobox' },
27
+ { tag: 'span', role: 'button' },
28
+ { tag: 'span', role: 'combobox' },
29
+ { tag: 'input', role: 'combobox' },
30
+ ];
31
+
32
+ const CONTAINMENT_THRESHOLD = 0.99; // 99% containment required
33
+
34
+ /**
35
+ * Check if element is a propagating element
36
+ */
37
+ function isPropagatingElement(element: ElementInfo): boolean {
38
+ const tagName = element.tagName || element.type || '';
39
+ const tag = tagName.toLowerCase();
40
+ const role = element.role || element.attributes?.role || null;
41
+
42
+ // For generic elements with cursor=pointer, we need to be more selective
43
+ // Only treat them as propagating if they don't have text content
44
+ // (text-containing generics are usually labels, not containers)
45
+ if ((tag === 'generic' || element.type === 'generic') &&
46
+ element.attributes?.['cursor'] === 'pointer') {
47
+ // If element has direct text content, it's likely a label, not a container
48
+ if (element.text && element.text.trim()) {
49
+ return false;
50
+ }
51
+ // If no text, it might be a container
52
+ return true;
53
+ }
54
+
55
+ for (const pattern of PROPAGATING_ELEMENTS) {
56
+ if (pattern.tag === tag) {
57
+ if (pattern.role === null || pattern.role === role) {
58
+ return true;
59
+ }
60
+ }
61
+ }
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Check if child bounds are contained within parent bounds
67
+ */
68
+ function isContained(
69
+ childBounds: { x: number; y: number; width: number; height: number },
70
+ parentBounds: { x: number; y: number; width: number; height: number },
71
+ threshold: number
72
+ ): boolean {
73
+ // Calculate intersection
74
+ const xOverlap = Math.max(0,
75
+ Math.min(childBounds.x + childBounds.width, parentBounds.x + parentBounds.width) -
76
+ Math.max(childBounds.x, parentBounds.x)
77
+ );
78
+ const yOverlap = Math.max(0,
79
+ Math.min(childBounds.y + childBounds.height, parentBounds.y + parentBounds.height) -
80
+ Math.max(childBounds.y, parentBounds.y)
81
+ );
82
+
83
+ const intersectionArea = xOverlap * yOverlap;
84
+ const childArea = childBounds.width * childBounds.height;
85
+
86
+ if (childArea === 0) return false;
87
+
88
+ return (intersectionArea / childArea) >= threshold;
89
+ }
90
+
91
+ /**
92
+ * Check if child element should be filtered out
93
+ */
94
+ function shouldFilterChild(childEl: ElementInfo, parentEl: ElementInfo): boolean {
95
+ // Never filter if parent is not a propagating element
96
+ if (!isPropagatingElement(parentEl)) {
97
+ return false;
98
+ }
99
+
100
+ // Never filter if elements don't have coordinates
101
+ if (!childEl.coordinates || !parentEl.coordinates) {
102
+ return false;
103
+ }
104
+
105
+ // Check containment
106
+ if (!isContained(childEl.coordinates, parentEl.coordinates, CONTAINMENT_THRESHOLD)) {
107
+ return false;
108
+ }
109
+
110
+ const childTag = (childEl.tagName || childEl.type || '').toLowerCase();
111
+ const childRole = childEl.role || childEl.attributes?.role || null;
112
+
113
+ // Exception rules - never filter these:
114
+
115
+ // 1. Form elements (need individual interaction)
116
+ if (['input', 'select', 'textarea', 'label'].includes(childTag)) {
117
+ return false;
118
+ }
119
+
120
+ // 2. Child is also a propagating element (might have stopPropagation)
121
+ if (isPropagatingElement(childEl)) {
122
+ return false;
123
+ }
124
+
125
+ // 3. Has onclick handler
126
+ if (childEl.attributes?.onclick) {
127
+ return false;
128
+ }
129
+
130
+ // 4. Has meaningful aria-label
131
+ if (childEl.attributes?.['aria-label']?.trim()) {
132
+ return false;
133
+ }
134
+
135
+ // 5. Has interactive role
136
+ if (['button', 'link', 'checkbox', 'radio', 'tab', 'menuitem'].includes(childRole || '')) {
137
+ return false;
138
+ }
139
+
140
+ // Default: filter this child
141
+ return true;
142
+ }
143
+
144
+ /**
145
+ * Filter clickable elements based on parent-child relationships
146
+ * @param elements - Map of all elements with their info
147
+ * @param clickableRefs - Set of refs that are clickable
148
+ * @returns Filtered set of element refs and debug info
149
+ */
150
+ export function filterParentChildElements(
151
+ elements: Record<string, ElementInfo>,
152
+ clickableRefs: Set<string>
153
+ ): {
154
+ filteredElements: Set<string>;
155
+ debugInfo: any[];
156
+ } {
157
+ const elementRefs = Array.from(clickableRefs);
158
+ const filteredElements = new Set<string>(elementRefs);
159
+ const debugInfo: any[] = [];
160
+
161
+ console.log(`[Parent-Child Filter] Analyzing ${elementRefs.length} clickable elements`);
162
+
163
+ // Check each pair of elements for parent-child filtering
164
+ for (let i = 0; i < elementRefs.length; i++) {
165
+ const parentRef = elementRefs[i];
166
+ const parentEl = elements[parentRef];
167
+
168
+ if (!parentEl?.coordinates) continue;
169
+
170
+ const isParentPropagating = isPropagatingElement(parentEl);
171
+
172
+ for (let j = 0; j < elementRefs.length; j++) {
173
+ if (i === j) continue;
174
+
175
+ const childRef = elementRefs[j];
176
+ const childEl = elements[childRef];
177
+
178
+ if (!childEl?.coordinates) continue;
179
+
180
+ // Debug parent-child relationships when enabled
181
+ const DEBUG_PARENT_CHILD = process.env.DEBUG_PARENT_CHILD === 'true';
182
+ if (DEBUG_PARENT_CHILD) {
183
+ const shouldFilter = shouldFilterChild(childEl, parentEl);
184
+ console.log(`\n[Debug] Checking ${parentRef} -> ${childRef}:`);
185
+ console.log(`Parent:`, {
186
+ ref: parentRef,
187
+ type: parentEl.type || parentEl.tagName,
188
+ role: parentEl.role,
189
+ coords: parentEl.coordinates,
190
+ isPropagating: isParentPropagating
191
+ });
192
+ console.log(`Child:`, {
193
+ ref: childRef,
194
+ type: childEl.type || childEl.tagName,
195
+ role: childEl.role,
196
+ coords: childEl.coordinates
197
+ });
198
+ console.log(`Should filter? ${shouldFilter}`);
199
+ }
200
+
201
+ if (shouldFilterChild(childEl, parentEl)) {
202
+ filteredElements.delete(childRef);
203
+
204
+ debugInfo.push({
205
+ type: 'filtered',
206
+ childRef,
207
+ parentRef,
208
+ reason: 'Contained within propagating parent',
209
+ parentType: parentEl.type || parentEl.tagName,
210
+ childType: childEl.type || childEl.tagName,
211
+ parentRole: parentEl.role,
212
+ childRole: childEl.role,
213
+ containment: isContained(childEl.coordinates, parentEl.coordinates, CONTAINMENT_THRESHOLD)
214
+ });
215
+ }
216
+ }
217
+ }
218
+
219
+ const filteredCount = elementRefs.length - filteredElements.size;
220
+ console.log(`[Parent-Child Filter] Filtered out ${filteredCount} child elements`);
221
+
222
+ return {
223
+ filteredElements,
224
+ debugInfo
225
+ };
226
+ }
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Parse snapshot text to extract parent-child relationships
3
+ */
4
+
5
+ export interface SnapshotNode {
6
+ ref: string;
7
+ type: string;
8
+ text?: string;
9
+ attributes: Record<string, string>;
10
+ children: string[]; // refs of child elements
11
+ parent?: string; // ref of parent element
12
+ }
13
+
14
+ export function parseSnapshotHierarchy(snapshotText: string): Map<string, SnapshotNode> {
15
+ const nodes = new Map<string, SnapshotNode>();
16
+ const lines = snapshotText.split('\n');
17
+
18
+ // Stack to track parent elements at each indentation level
19
+ const parentStack: { ref: string; indent: number }[] = [];
20
+
21
+ for (const line of lines) {
22
+ if (!line.trim()) continue;
23
+
24
+ // Calculate indentation
25
+ const indent = line.length - line.trimStart().length;
26
+
27
+ // Extract element info using regex
28
+ // Support both lines with : (have children) and without : (leaf nodes)
29
+ // Also support quoted lines like - 'button "text" [ref=...]'
30
+ // Also support escaped quotes in text
31
+ // Also support attributes before [ref=...]
32
+ // Extract type and optional label (before any [..] blocks)
33
+ const headerMatch = line.match(/^\s*(?:-\s*)?'?([a-z0-9_-]+)(?:\s+"((?:[^"\\]|\\.)*)")?/i);
34
+ if (!headerMatch) continue;
35
+ const [, typeRaw, label] = headerMatch;
36
+ const type = (typeRaw || 'unknown');
37
+
38
+ // Extract mandatory ref
39
+ const refMatch = line.match(/\[ref=([^\]]+)\]/i);
40
+ if (!refMatch) continue;
41
+ const ref = refMatch[1];
42
+
43
+ // Parse all bracketed attributes except the [ref=...] block
44
+ const attrs: Record<string, string> = {};
45
+ for (const block of line.matchAll(/\[([^\]]+)\]/g)) {
46
+ const content = block[1];
47
+ if (/^ref=/i.test(content)) continue;
48
+ const attrMatches = content.matchAll(/([a-z0-9_-]+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s\]]+)))?/gi);
49
+ for (const m of attrMatches) {
50
+ const key = m[1].toLowerCase();
51
+ const val = m[2] ?? m[3] ?? m[4] ?? 'true';
52
+ attrs[key] = val;
53
+ }
54
+ }
55
+
56
+ // Update parent stack based on indentation
57
+ while (parentStack.length > 0 && parentStack[parentStack.length - 1].indent >= indent) {
58
+ parentStack.pop();
59
+ }
60
+
61
+ // Create node
62
+ const node: SnapshotNode = {
63
+ ref,
64
+ type: type.toLowerCase(),
65
+ text: label || '',
66
+ attributes: attrs,
67
+ children: [],
68
+ parent: parentStack.length > 0 ? parentStack[parentStack.length - 1].ref : undefined
69
+ };
70
+
71
+ // Add to parent's children if has parent
72
+ if (node.parent && nodes.has(node.parent)) {
73
+ nodes.get(node.parent)!.children.push(ref);
74
+ }
75
+
76
+ // Add to nodes map
77
+ nodes.set(ref, node);
78
+
79
+ // Add to parent stack
80
+ parentStack.push({ ref, indent });
81
+ }
82
+
83
+ return nodes;
84
+ }
85
+
86
+ /**
87
+ * Find clickable elements that should be filtered based on DOM hierarchy
88
+ */
89
+ export function filterClickableByHierarchy(
90
+ snapshotText: string,
91
+ clickableElements: Set<string>
92
+ ): Set<string> {
93
+ const hierarchy = parseSnapshotHierarchy(snapshotText);
94
+ const filtered = new Set<string>(clickableElements);
95
+ const debugInfo: any[] = [];
96
+
97
+ // Debug clickable elements when enabled
98
+ const DEBUG_SNAPSHOT_PARSER = process.env.DEBUG_SNAPSHOT_PARSER === 'true';
99
+ if (DEBUG_SNAPSHOT_PARSER) {
100
+ clickableElements.forEach(ref => {
101
+ const node = hierarchy.get(ref);
102
+ if (node) {
103
+ console.log(`[Debug] ${ref} type: ${node.type}, parent: ${node.parent || 'none'}`);
104
+ } else {
105
+ console.log(`[Debug] ${ref} NOT FOUND in hierarchy!`);
106
+ }
107
+ });
108
+ }
109
+
110
+ // First pass: identify parent-child relationships where both are clickable
111
+ const parentChildPairs: Array<{parent: string, child: string, parentType: string, childType: string}> = [];
112
+
113
+ for (const childRef of clickableElements) {
114
+ const childNode = hierarchy.get(childRef);
115
+ if (!childNode || !childNode.parent) continue;
116
+
117
+ const parentRef = childNode.parent;
118
+ if (clickableElements.has(parentRef)) {
119
+ const parentNode = hierarchy.get(parentRef);
120
+ if (parentNode) {
121
+ parentChildPairs.push({
122
+ parent: parentRef,
123
+ child: childRef,
124
+ parentType: parentNode.type.toLowerCase(),
125
+ childType: childNode.type.toLowerCase()
126
+ });
127
+
128
+ // Debug specific pairs
129
+ if ((parentRef === 'e296' && childRef === 'e297') ||
130
+ (parentRef === 'e361' && childRef === 'e363') ||
131
+ (parentRef === 'e371' && childRef === 'e373') ||
132
+ (parentRef === 'e344' && childRef === 'e346') ||
133
+ (parentRef === 'e348' && childRef === 'e350')) {
134
+ console.log(`[Debug] Found pair: ${parentRef} (${parentNode.type}) -> ${childRef} (${childNode.type})`);
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ // Decide which elements to filter based on parent-child relationships
141
+ for (const pair of parentChildPairs) {
142
+ const { parent, child, parentType, childType } = pair;
143
+
144
+ // Rules for what to filter:
145
+ // 1. link > img: filter img (keep link)
146
+ // 2. button > generic: filter generic (keep button)
147
+ // 3. generic > button: filter generic (keep button)
148
+ // 4. link > generic: filter generic (keep link)
149
+ // 5. generic > generic: filter child (keep parent)
150
+ // 6. generic > unknown: filter child (keep parent)
151
+
152
+ if ((parentType === 'link' && childType === 'img') ||
153
+ (parentType === 'button' && childType === 'generic') ||
154
+ (parentType === 'link' && childType === 'generic') ||
155
+ (parentType === 'generic' && childType === 'generic') ||
156
+ (parentType === 'generic' && childType === 'unknown')) {
157
+ // Filter child
158
+ filtered.delete(child);
159
+ console.log(`[Hierarchy Filter] Filtered ${child} (${childType}) - keeping parent ${parent} (${parentType})`);
160
+ } else if (parentType === 'generic' && childType === 'button') {
161
+ // Special case: filter parent generic, keep child button
162
+ filtered.delete(parent);
163
+ console.log(`[Hierarchy Filter] Filtered ${parent} (${parentType}) - keeping child ${child} (${childType})`);
164
+ }
165
+ }
166
+
167
+ // Original logic for nested hierarchies (keep for deep nesting)
168
+ for (const childRef of clickableElements) {
169
+ if (!filtered.has(childRef)) continue; // Already filtered
170
+
171
+ const childNode = hierarchy.get(childRef);
172
+ if (!childNode || !childNode.parent) continue;
173
+
174
+ // Check if any ancestor is a propagating element
175
+ let currentParent: string | undefined = childNode.parent;
176
+ while (currentParent) {
177
+ if (clickableElements.has(currentParent) && filtered.has(currentParent)) {
178
+ const parentNode = hierarchy.get(currentParent);
179
+ if (parentNode) {
180
+ // Check if parent is a propagating element
181
+ const parentType = parentNode.type.toLowerCase();
182
+ const isPropagating = ['button', 'link', 'a'].includes(parentType) ||
183
+ (parentType === 'generic' && parentNode.attributes.cursor === 'pointer' && !parentNode.text);
184
+
185
+ if (isPropagating) {
186
+ // Filter child elements that should be contained within propagating parents
187
+ const childType = childNode.type.toLowerCase();
188
+
189
+ // Filter these types of children:
190
+ // 1. Generic elements with cursor=pointer
191
+ // 2. Images within links/buttons
192
+ // 3. Text elements (span, generic without specific role)
193
+ const shouldFilter =
194
+ (childType === 'generic' && childNode.attributes.cursor === 'pointer') ||
195
+ childType === 'img' ||
196
+ childType === 'span' ||
197
+ (childType === 'generic' && !childNode.attributes.role);
198
+
199
+ if (shouldFilter) {
200
+ filtered.delete(childRef);
201
+ console.log(`[Hierarchy Filter] Filtered ${childRef} (${childType}) contained in ${currentParent} (${parentType})`);
202
+ break;
203
+ }
204
+ }
205
+ }
206
+ }
207
+ // Move up the hierarchy
208
+ const nextParent = hierarchy.get(currentParent);
209
+ currentParent = nextParent?.parent;
210
+ }
211
+ }
212
+
213
+ // Additional pass: if a generic parent contains only one button child, filter the parent
214
+ for (const ref of Array.from(filtered)) {
215
+ const node = hierarchy.get(ref);
216
+ if (!node || node.type.toLowerCase() !== 'generic') continue;
217
+
218
+ // Check if this generic has exactly one clickable child that's a button
219
+ const clickableChildren = node.children.filter(childRef =>
220
+ filtered.has(childRef) && hierarchy.get(childRef)?.type.toLowerCase() === 'button'
221
+ );
222
+
223
+ if (clickableChildren.length === 1) {
224
+ // This generic wraps a single button - filter it out
225
+ filtered.delete(ref);
226
+ console.log(`[Hierarchy Filter] Filtered ${ref} (generic wrapper around button ${clickableChildren[0]})`);
227
+ }
228
+ }
229
+
230
+ return filtered;
231
+ }