camel-ai 0.2.75a3__py3-none-any.whl → 0.2.75a5__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.
- camel/__init__.py +1 -1
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +35 -5
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +124 -29
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +1 -1
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +103 -40
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +3 -2
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +8 -1
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +55 -0
- camel/toolkits/openai_image_toolkit.py +55 -24
- camel/types/enums.py +3 -9
- {camel_ai-0.2.75a3.dist-info → camel_ai-0.2.75a5.dist-info}/METADATA +4 -3
- {camel_ai-0.2.75a3.dist-info → camel_ai-0.2.75a5.dist-info}/RECORD +14 -14
- {camel_ai-0.2.75a3.dist-info → camel_ai-0.2.75a5.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.75a3.dist-info → camel_ai-0.2.75a5.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
|
@@ -674,12 +674,29 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
|
|
|
674
674
|
"total_tabs": 0,
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
-
async def browser_type(
|
|
678
|
-
|
|
677
|
+
async def browser_type(
|
|
678
|
+
self,
|
|
679
|
+
*,
|
|
680
|
+
ref: Optional[str] = None,
|
|
681
|
+
text: Optional[str] = None,
|
|
682
|
+
inputs: Optional[List[Dict[str, str]]] = None,
|
|
683
|
+
) -> Dict[str, Any]:
|
|
684
|
+
r"""Types text into one or more input elements on the page.
|
|
685
|
+
|
|
686
|
+
This method supports two modes:
|
|
687
|
+
1. Single input mode (backward compatible): Provide 'ref' and 'text'
|
|
688
|
+
2. Multiple inputs mode: Provide 'inputs' as a list of dictionaries
|
|
689
|
+
with 'ref' and 'text' keys
|
|
679
690
|
|
|
680
691
|
Args:
|
|
681
|
-
ref (str): The `ref` ID of the input element, from a
|
|
682
|
-
|
|
692
|
+
ref (Optional[str]): The `ref` ID of the input element, from a
|
|
693
|
+
snapshot. Required when using single input mode.
|
|
694
|
+
text (Optional[str]): The text to type into the element. Required
|
|
695
|
+
when using single input mode.
|
|
696
|
+
inputs (Optional[List[Dict[str, str]]]): List of dictionaries,
|
|
697
|
+
each containing 'ref' and 'text' keys for typing into multiple
|
|
698
|
+
elements. Example: [{'ref': '1', 'text': 'username'},
|
|
699
|
+
{'ref': '2', 'text': 'password'}]
|
|
683
700
|
|
|
684
701
|
Returns:
|
|
685
702
|
Dict[str, Any]: A dictionary with the result of the action:
|
|
@@ -689,10 +706,23 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
|
|
|
689
706
|
- "tabs" (List[Dict]): Information about all open tabs.
|
|
690
707
|
- "current_tab" (int): Index of the active tab.
|
|
691
708
|
- "total_tabs" (int): Total number of open tabs.
|
|
709
|
+
- "details" (Dict[str, Any]): When using multiple inputs,
|
|
710
|
+
contains success/error status for each ref.
|
|
692
711
|
"""
|
|
693
712
|
try:
|
|
694
713
|
ws_wrapper = await self._get_ws_wrapper()
|
|
695
|
-
|
|
714
|
+
|
|
715
|
+
# Handle single input mode (backward compatibility)
|
|
716
|
+
if ref is not None and text is not None:
|
|
717
|
+
result = await ws_wrapper.type(ref, text)
|
|
718
|
+
# Handle multiple inputs mode
|
|
719
|
+
elif inputs is not None:
|
|
720
|
+
result = await ws_wrapper.type_multiple(inputs)
|
|
721
|
+
else:
|
|
722
|
+
raise ValueError(
|
|
723
|
+
"Either provide 'ref' and 'text' for single input, "
|
|
724
|
+
"or 'inputs' for multiple inputs"
|
|
725
|
+
)
|
|
696
726
|
|
|
697
727
|
# Add tab information
|
|
698
728
|
tab_info = await ws_wrapper.get_tab_info()
|
|
@@ -72,21 +72,19 @@ export class HybridBrowserSession {
|
|
|
72
72
|
this.context = await this.browser.newContext(contextOptions);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// Handle existing pages
|
|
76
75
|
const pages = this.context.pages();
|
|
77
76
|
if (pages.length > 0) {
|
|
78
|
-
// Map existing pages - for CDP,
|
|
77
|
+
// Map existing pages - for CDP, find ONE available blank page
|
|
79
78
|
let availablePageFound = false;
|
|
80
79
|
for (const page of pages) {
|
|
81
80
|
const pageUrl = page.url();
|
|
82
|
-
|
|
83
|
-
if (pageUrl === 'about:blank') {
|
|
81
|
+
if (this.isBlankPageUrl(pageUrl)) {
|
|
84
82
|
const tabId = this.generateTabId();
|
|
85
83
|
this.registerNewPage(tabId, page);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
this.currentTabId = tabId;
|
|
85
|
+
availablePageFound = true;
|
|
86
|
+
console.log(`[CDP] Registered blank page as initial tab: ${tabId}, URL: ${pageUrl}`);
|
|
87
|
+
break; // Only register ONE page initially
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
90
|
|
|
@@ -157,8 +155,45 @@ export class HybridBrowserSession {
|
|
|
157
155
|
return `${browserConfig.tabIdPrefix}${String(++this.tabCounter).padStart(browserConfig.tabCounterPadding, '0')}`;
|
|
158
156
|
}
|
|
159
157
|
|
|
158
|
+
private isBlankPageUrl(url: string): boolean {
|
|
159
|
+
// Unified blank page detection logic used across the codebase
|
|
160
|
+
const browserConfig = this.configLoader.getBrowserConfig();
|
|
161
|
+
return (
|
|
162
|
+
// Standard about:blank variations (prefix match for query params)
|
|
163
|
+
url === 'about:blank' ||
|
|
164
|
+
url.startsWith('about:blank?') ||
|
|
165
|
+
// Configured blank page URLs (exact match for compatibility)
|
|
166
|
+
browserConfig.blankPageUrls.includes(url) ||
|
|
167
|
+
// Empty URL
|
|
168
|
+
url === '' ||
|
|
169
|
+
// Data URLs (often used for blank pages)
|
|
170
|
+
url.startsWith(browserConfig.dataUrlPrefix || 'data:')
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
160
174
|
async getCurrentPage(): Promise<Page> {
|
|
161
175
|
if (!this.currentTabId || !this.pages.has(this.currentTabId)) {
|
|
176
|
+
// In CDP mode, try to create a new page if none exists
|
|
177
|
+
const browserConfig = this.configLoader.getBrowserConfig();
|
|
178
|
+
if (browserConfig.connectOverCdp && this.context) {
|
|
179
|
+
console.log('[CDP] No active page found, attempting to create new page...');
|
|
180
|
+
try {
|
|
181
|
+
const newPage = await this.context.newPage();
|
|
182
|
+
const newTabId = this.generateTabId();
|
|
183
|
+
this.registerNewPage(newTabId, newPage);
|
|
184
|
+
this.currentTabId = newTabId;
|
|
185
|
+
|
|
186
|
+
// Set page timeouts
|
|
187
|
+
newPage.setDefaultNavigationTimeout(browserConfig.navigationTimeout);
|
|
188
|
+
newPage.setDefaultTimeout(browserConfig.navigationTimeout);
|
|
189
|
+
|
|
190
|
+
console.log(`[CDP] Created new page with tab ID: ${newTabId}`);
|
|
191
|
+
return newPage;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error('[CDP] Failed to create new page:', error);
|
|
194
|
+
throw new Error('No active page available and failed to create new page in CDP mode');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
162
197
|
throw new Error('No active page available');
|
|
163
198
|
}
|
|
164
199
|
return this.pages.get(this.currentTabId)!;
|
|
@@ -416,25 +451,67 @@ export class HybridBrowserSession {
|
|
|
416
451
|
|
|
417
452
|
/**
|
|
418
453
|
* Simplified type implementation using Playwright's aria-ref selector
|
|
454
|
+
* Supports both single and multiple input operations
|
|
419
455
|
*/
|
|
420
|
-
private async performType(page: Page, ref: string, text: string): Promise<{ success: boolean; error?: string }> {
|
|
456
|
+
private async performType(page: Page, ref: string | undefined, text: string | undefined, inputs?: Array<{ ref: string; text: string }>): Promise<{ success: boolean; error?: string; details?: Record<string, any> }> {
|
|
421
457
|
try {
|
|
422
458
|
// Ensure we have the latest snapshot
|
|
423
459
|
await (page as any)._snapshotForAI();
|
|
424
460
|
|
|
425
|
-
//
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
461
|
+
// Handle multiple inputs if provided
|
|
462
|
+
if (inputs && inputs.length > 0) {
|
|
463
|
+
const results: Record<string, { success: boolean; error?: string }> = {};
|
|
464
|
+
|
|
465
|
+
for (const input of inputs) {
|
|
466
|
+
const selector = `aria-ref=${input.ref}`;
|
|
467
|
+
const element = await page.locator(selector).first();
|
|
468
|
+
|
|
469
|
+
const exists = await element.count() > 0;
|
|
470
|
+
if (!exists) {
|
|
471
|
+
results[input.ref] = { success: false, error: `Element with ref ${input.ref} not found` };
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
// Type text using Playwright's built-in fill method
|
|
477
|
+
await element.fill(input.text);
|
|
478
|
+
results[input.ref] = { success: true };
|
|
479
|
+
} catch (error) {
|
|
480
|
+
results[input.ref] = { success: false, error: `Type failed: ${error}` };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Check if all inputs were successful
|
|
485
|
+
const allSuccess = Object.values(results).every(r => r.success);
|
|
486
|
+
const errors = Object.entries(results)
|
|
487
|
+
.filter(([_, r]) => !r.success)
|
|
488
|
+
.map(([ref, r]) => `${ref}: ${r.error}`)
|
|
489
|
+
.join('; ');
|
|
490
|
+
|
|
491
|
+
return {
|
|
492
|
+
success: allSuccess,
|
|
493
|
+
error: allSuccess ? undefined : `Some inputs failed: ${errors}`,
|
|
494
|
+
details: results
|
|
495
|
+
};
|
|
432
496
|
}
|
|
433
497
|
|
|
434
|
-
//
|
|
435
|
-
|
|
498
|
+
// Handle single input (backward compatibility)
|
|
499
|
+
if (ref && text !== undefined) {
|
|
500
|
+
const selector = `aria-ref=${ref}`;
|
|
501
|
+
const element = await page.locator(selector).first();
|
|
502
|
+
|
|
503
|
+
const exists = await element.count() > 0;
|
|
504
|
+
if (!exists) {
|
|
505
|
+
return { success: false, error: `Element with ref ${ref} not found` };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Type text using Playwright's built-in fill method
|
|
509
|
+
await element.fill(text);
|
|
510
|
+
|
|
511
|
+
return { success: true };
|
|
512
|
+
}
|
|
436
513
|
|
|
437
|
-
return { success:
|
|
514
|
+
return { success: false, error: 'No valid input provided' };
|
|
438
515
|
} catch (error) {
|
|
439
516
|
return { success: false, error: `Type failed: ${error}` };
|
|
440
517
|
}
|
|
@@ -572,6 +649,8 @@ export class HybridBrowserSession {
|
|
|
572
649
|
// No need to pre-fetch snapshot - each action method handles this
|
|
573
650
|
|
|
574
651
|
let newTabId: string | undefined;
|
|
652
|
+
let customMessage: string | undefined;
|
|
653
|
+
let actionDetails: Record<string, any> | undefined;
|
|
575
654
|
|
|
576
655
|
switch (action.type) {
|
|
577
656
|
case 'click': {
|
|
@@ -596,12 +675,20 @@ export class HybridBrowserSession {
|
|
|
596
675
|
elementSearchTime = Date.now() - elementSearchStart;
|
|
597
676
|
const typeStart = Date.now();
|
|
598
677
|
|
|
599
|
-
const typeResult = await this.performType(page, action.ref, action.text);
|
|
678
|
+
const typeResult = await this.performType(page, action.ref, action.text, action.inputs);
|
|
600
679
|
|
|
601
680
|
if (!typeResult.success) {
|
|
602
681
|
throw new Error(`Type failed: ${typeResult.error}`);
|
|
603
682
|
}
|
|
604
683
|
|
|
684
|
+
// Set custom message and details if multiple inputs were used
|
|
685
|
+
if (typeResult.details) {
|
|
686
|
+
const successCount = Object.values(typeResult.details).filter((r: any) => r.success).length;
|
|
687
|
+
const totalCount = Object.keys(typeResult.details).length;
|
|
688
|
+
customMessage = `Typed text into ${successCount}/${totalCount} elements`;
|
|
689
|
+
actionDetails = typeResult.details;
|
|
690
|
+
}
|
|
691
|
+
|
|
605
692
|
actionExecutionTime = Date.now() - typeStart;
|
|
606
693
|
break;
|
|
607
694
|
}
|
|
@@ -689,7 +776,7 @@ export class HybridBrowserSession {
|
|
|
689
776
|
|
|
690
777
|
return {
|
|
691
778
|
success: true,
|
|
692
|
-
message: `Action ${action.type} executed successfully`,
|
|
779
|
+
message: customMessage || `Action ${action.type} executed successfully`,
|
|
693
780
|
timing: {
|
|
694
781
|
total_time_ms: totalTime,
|
|
695
782
|
element_search_time_ms: elementSearchTime,
|
|
@@ -699,6 +786,7 @@ export class HybridBrowserSession {
|
|
|
699
786
|
network_idle_time_ms: stabilityResult.networkIdleTime,
|
|
700
787
|
},
|
|
701
788
|
...(newTabId && { newTabId }), // Include new tab ID if present
|
|
789
|
+
...(actionDetails && { details: actionDetails }), // Include action details if present
|
|
702
790
|
};
|
|
703
791
|
} catch (error) {
|
|
704
792
|
const totalTime = Date.now() - startTime;
|
|
@@ -740,16 +828,23 @@ export class HybridBrowserSession {
|
|
|
740
828
|
|
|
741
829
|
try {
|
|
742
830
|
// Get current page to check if it's blank
|
|
743
|
-
|
|
744
|
-
|
|
831
|
+
let currentPage: Page;
|
|
832
|
+
let currentUrl: string;
|
|
833
|
+
|
|
834
|
+
try {
|
|
835
|
+
currentPage = await this.getCurrentPage();
|
|
836
|
+
currentUrl = currentPage.url();
|
|
837
|
+
} catch (error: any) {
|
|
838
|
+
// If no active page is available, getCurrentPage() will create one in CDP mode
|
|
839
|
+
console.log('[visitPage] Failed to get current page:', error);
|
|
840
|
+
throw new Error(`No active page available: ${error?.message || error}`);
|
|
841
|
+
}
|
|
745
842
|
|
|
746
843
|
// Check if current page is blank or if this is the first navigation
|
|
747
844
|
const browserConfig = this.configLoader.getBrowserConfig();
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
currentUrl.startsWith(browserConfig.dataUrlPrefix) // data URLs are often used for blank pages
|
|
752
|
-
);
|
|
845
|
+
|
|
846
|
+
// Use unified blank page detection
|
|
847
|
+
const isBlankPage = this.isBlankPageUrl(currentUrl) || currentUrl === browserConfig.defaultStartUrl;
|
|
753
848
|
|
|
754
849
|
const shouldUseCurrentTab = isBlankPage || !this.hasNavigatedBefore;
|
|
755
850
|
|
|
@@ -804,7 +899,7 @@ export class HybridBrowserSession {
|
|
|
804
899
|
const pageUrl = page.url();
|
|
805
900
|
// Check if this page is not already tracked and is blank
|
|
806
901
|
const isTracked = Array.from(this.pages.values()).includes(page);
|
|
807
|
-
if (!isTracked && pageUrl
|
|
902
|
+
if (!isTracked && this.isBlankPageUrl(pageUrl)) {
|
|
808
903
|
newPage = page;
|
|
809
904
|
newTabId = this.generateTabId();
|
|
810
905
|
this.registerNewPage(newTabId, newPage);
|
|
@@ -117,7 +117,7 @@ function getDefaultBrowserConfig(): BrowserConfig {
|
|
|
117
117
|
consoleLogLimit: 1000,
|
|
118
118
|
scrollPositionScale: 0.1,
|
|
119
119
|
navigationDelay: 100,
|
|
120
|
-
blankPageUrls: [
|
|
120
|
+
blankPageUrls: [],
|
|
121
121
|
dataUrlPrefix: 'data:',
|
|
122
122
|
domContentLoadedState: 'domcontentloaded',
|
|
123
123
|
networkIdleState: 'networkidle',
|
|
@@ -69,35 +69,52 @@ export class HybridBrowserToolkit {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
async visitPage(url: string): Promise<any> {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
result
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
try {
|
|
73
|
+
// Ensure browser is initialized before visiting page
|
|
74
|
+
await this.session.ensureBrowser();
|
|
75
|
+
|
|
76
|
+
const result = await this.session.visitPage(url);
|
|
77
|
+
|
|
78
|
+
// Format response for Python layer compatibility
|
|
79
|
+
const response: any = {
|
|
80
|
+
result: result.message,
|
|
81
|
+
snapshot: '',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (result.success) {
|
|
85
|
+
const snapshotStart = Date.now();
|
|
86
|
+
response.snapshot = await this.getPageSnapshot(this.viewportLimit);
|
|
87
|
+
const snapshotTime = Date.now() - snapshotStart;
|
|
88
|
+
|
|
89
|
+
if (result.timing) {
|
|
90
|
+
result.timing.snapshot_time_ms = snapshotTime;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
84
93
|
|
|
94
|
+
// Include timing if available
|
|
85
95
|
if (result.timing) {
|
|
86
|
-
|
|
96
|
+
response.timing = result.timing;
|
|
87
97
|
}
|
|
98
|
+
|
|
99
|
+
// Include newTabId if present
|
|
100
|
+
if (result.newTabId) {
|
|
101
|
+
response.newTabId = result.newTabId;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return response;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('[visitPage] Error:', error);
|
|
107
|
+
return {
|
|
108
|
+
result: `Navigation to ${url} failed: ${error}`,
|
|
109
|
+
snapshot: '',
|
|
110
|
+
timing: {
|
|
111
|
+
total_time_ms: 0,
|
|
112
|
+
navigation_time_ms: 0,
|
|
113
|
+
dom_content_loaded_time_ms: 0,
|
|
114
|
+
network_idle_time_ms: 0,
|
|
115
|
+
}
|
|
116
|
+
};
|
|
88
117
|
}
|
|
89
|
-
|
|
90
|
-
// Include timing if available
|
|
91
|
-
if (result.timing) {
|
|
92
|
-
response.timing = result.timing;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Include newTabId if present
|
|
96
|
-
if (result.newTabId) {
|
|
97
|
-
response.newTabId = result.newTabId;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return response;
|
|
101
118
|
}
|
|
102
119
|
|
|
103
120
|
async getPageSnapshot(viewportLimit: boolean = false): Promise<string> {
|
|
@@ -179,7 +196,40 @@ export class HybridBrowserToolkit {
|
|
|
179
196
|
// Use sharp for image processing
|
|
180
197
|
const sharp = require('sharp');
|
|
181
198
|
const page = await this.session.getCurrentPage();
|
|
182
|
-
|
|
199
|
+
let viewport = page.viewportSize();
|
|
200
|
+
|
|
201
|
+
// In CDP mode, viewportSize might be null, get it from window dimensions
|
|
202
|
+
if (!viewport) {
|
|
203
|
+
const windowSize = await page.evaluate(() => ({
|
|
204
|
+
width: window.innerWidth,
|
|
205
|
+
height: window.innerHeight
|
|
206
|
+
}));
|
|
207
|
+
viewport = windowSize;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Get device pixel ratio to handle high DPI screens
|
|
211
|
+
const dpr = await page.evaluate(() => window.devicePixelRatio) || 1;
|
|
212
|
+
|
|
213
|
+
// Get actual screenshot dimensions
|
|
214
|
+
const metadata = await sharp(screenshotBuffer).metadata();
|
|
215
|
+
const screenshotWidth = metadata.width || viewport.width;
|
|
216
|
+
const screenshotHeight = metadata.height || viewport.height;
|
|
217
|
+
|
|
218
|
+
// Calculate scaling factor between CSS pixels and screenshot pixels
|
|
219
|
+
const scaleX = screenshotWidth / viewport.width;
|
|
220
|
+
const scaleY = screenshotHeight / viewport.height;
|
|
221
|
+
|
|
222
|
+
// Debug logging for CDP mode
|
|
223
|
+
if (process.env.HYBRID_BROWSER_DEBUG === '1') {
|
|
224
|
+
console.log('[CDP Debug] Viewport size:', viewport);
|
|
225
|
+
console.log('[CDP Debug] Device pixel ratio:', dpr);
|
|
226
|
+
console.log('[CDP Debug] Screenshot dimensions:', { width: screenshotWidth, height: screenshotHeight });
|
|
227
|
+
console.log('[CDP Debug] Scale factors:', { scaleX, scaleY });
|
|
228
|
+
console.log('[CDP Debug] Elements with coordinates:', elementsWithCoords.length);
|
|
229
|
+
elementsWithCoords.slice(0, 3).forEach(([ref, element]) => {
|
|
230
|
+
console.log(`[CDP Debug] Element ${ref}:`, element.coordinates);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
183
233
|
|
|
184
234
|
// Filter elements visible in viewport
|
|
185
235
|
const visibleElements = elementsWithCoords.filter(([ref, element]) => {
|
|
@@ -198,18 +248,19 @@ export class HybridBrowserToolkit {
|
|
|
198
248
|
const coords = element.coordinates!;
|
|
199
249
|
const isClickable = clickableElements.has(ref);
|
|
200
250
|
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
251
|
+
// Scale coordinates from CSS pixels to screenshot pixels
|
|
252
|
+
const x = Math.max(0, coords.x * scaleX);
|
|
253
|
+
const y = Math.max(0, coords.y * scaleY);
|
|
254
|
+
const width = coords.width * scaleX;
|
|
255
|
+
const height = coords.height * scaleY;
|
|
256
|
+
|
|
257
|
+
// Clamp to screenshot bounds
|
|
258
|
+
const clampedWidth = Math.min(width, screenshotWidth - x);
|
|
259
|
+
const clampedHeight = Math.min(height, screenshotHeight - y);
|
|
209
260
|
|
|
210
261
|
// Position text to be visible even if element is partially cut off
|
|
211
|
-
const textX = Math.max(2, Math.min(x + 2,
|
|
212
|
-
const textY = Math.max(14, Math.min(y + 14,
|
|
262
|
+
const textX = Math.max(2, Math.min(x + 2, screenshotWidth - 40));
|
|
263
|
+
const textY = Math.max(14, Math.min(y + 14, screenshotHeight - 4));
|
|
213
264
|
|
|
214
265
|
// Different colors for clickable vs non-clickable elements
|
|
215
266
|
const colors = isClickable ? {
|
|
@@ -223,7 +274,7 @@ export class HybridBrowserToolkit {
|
|
|
223
274
|
};
|
|
224
275
|
|
|
225
276
|
return `
|
|
226
|
-
<rect x="${x}" y="${y}" width="${
|
|
277
|
+
<rect x="${x}" y="${y}" width="${clampedWidth}" height="${clampedHeight}"
|
|
227
278
|
fill="${colors.fill}" stroke="${colors.stroke}" stroke-width="2" rx="2"/>
|
|
228
279
|
<text x="${textX}" y="${textY}" font-family="Arial, sans-serif"
|
|
229
280
|
font-size="12" fill="${colors.textFill}" font-weight="bold">${ref}</text>
|
|
@@ -231,7 +282,7 @@ export class HybridBrowserToolkit {
|
|
|
231
282
|
}).join('');
|
|
232
283
|
|
|
233
284
|
const svgOverlay = `
|
|
234
|
-
<svg width="${
|
|
285
|
+
<svg width="${screenshotWidth}" height="${screenshotHeight}" xmlns="http://www.w3.org/2000/svg">
|
|
235
286
|
${marks}
|
|
236
287
|
</svg>
|
|
237
288
|
`;
|
|
@@ -363,8 +414,20 @@ export class HybridBrowserToolkit {
|
|
|
363
414
|
return this.executeActionWithSnapshot(action);
|
|
364
415
|
}
|
|
365
416
|
|
|
366
|
-
async type(ref: string
|
|
367
|
-
|
|
417
|
+
async type(refOrInputs: string | Array<{ ref: string; text: string }>, text?: string): Promise<any> {
|
|
418
|
+
let action: BrowserAction;
|
|
419
|
+
|
|
420
|
+
if (typeof refOrInputs === 'string') {
|
|
421
|
+
// Single input mode (backward compatibility)
|
|
422
|
+
if (text === undefined) {
|
|
423
|
+
throw new Error('Text parameter is required when ref is a string');
|
|
424
|
+
}
|
|
425
|
+
action = { type: 'type', ref: refOrInputs, text };
|
|
426
|
+
} else {
|
|
427
|
+
// Multiple inputs mode
|
|
428
|
+
action = { type: 'type', inputs: refOrInputs };
|
|
429
|
+
}
|
|
430
|
+
|
|
368
431
|
return this.executeActionWithSnapshot(action);
|
|
369
432
|
}
|
|
370
433
|
|
|
@@ -81,8 +81,9 @@ export interface ClickAction {
|
|
|
81
81
|
|
|
82
82
|
export interface TypeAction {
|
|
83
83
|
type: 'type';
|
|
84
|
-
ref
|
|
85
|
-
text
|
|
84
|
+
ref?: string; // Optional for backward compatibility
|
|
85
|
+
text?: string; // Optional for backward compatibility
|
|
86
|
+
inputs?: Array<{ ref: string; text: string }>; // New field for multiple inputs
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
export interface SelectAction {
|
|
@@ -160,7 +160,14 @@ class WebSocketBrowserServer {
|
|
|
160
160
|
|
|
161
161
|
case 'type':
|
|
162
162
|
if (!this.toolkit) throw new Error('Toolkit not initialized');
|
|
163
|
-
|
|
163
|
+
// Handle both single input and multiple inputs
|
|
164
|
+
if (params.inputs) {
|
|
165
|
+
// Multiple inputs mode - pass inputs array directly
|
|
166
|
+
return await this.toolkit.type(params.inputs);
|
|
167
|
+
} else {
|
|
168
|
+
// Single input mode - pass ref and text
|
|
169
|
+
return await this.toolkit.type(params.ref, params.text);
|
|
170
|
+
}
|
|
164
171
|
|
|
165
172
|
case 'select':
|
|
166
173
|
if (!this.toolkit) throw new Error('Toolkit not initialized');
|
|
@@ -396,6 +396,9 @@ class WebSocketBrowserWrapper:
|
|
|
396
396
|
"""Send a command to the WebSocket server and get response."""
|
|
397
397
|
await self._ensure_connection()
|
|
398
398
|
|
|
399
|
+
# Process params to ensure refs have 'e' prefix
|
|
400
|
+
params = self._process_refs_in_params(params)
|
|
401
|
+
|
|
399
402
|
message_id = str(uuid.uuid4())
|
|
400
403
|
message = {'id': message_id, 'command': command, 'params': params}
|
|
401
404
|
|
|
@@ -503,6 +506,50 @@ class WebSocketBrowserWrapper:
|
|
|
503
506
|
|
|
504
507
|
return ToolResult(text=response['text'], images=response['images'])
|
|
505
508
|
|
|
509
|
+
def _ensure_ref_prefix(self, ref: str) -> str:
|
|
510
|
+
"""Ensure ref has 'e' prefix."""
|
|
511
|
+
if ref and not ref.startswith('e'):
|
|
512
|
+
return f'e{ref}'
|
|
513
|
+
return ref
|
|
514
|
+
|
|
515
|
+
def _process_refs_in_params(
|
|
516
|
+
self, params: Dict[str, Any]
|
|
517
|
+
) -> Dict[str, Any]:
|
|
518
|
+
"""Process parameters to ensure all refs have 'e' prefix."""
|
|
519
|
+
if not params:
|
|
520
|
+
return params
|
|
521
|
+
|
|
522
|
+
# Create a copy to avoid modifying the original
|
|
523
|
+
processed = params.copy()
|
|
524
|
+
|
|
525
|
+
# Handle direct ref parameters
|
|
526
|
+
if 'ref' in processed:
|
|
527
|
+
processed['ref'] = self._ensure_ref_prefix(processed['ref'])
|
|
528
|
+
|
|
529
|
+
# Handle from_ref and to_ref for drag operations
|
|
530
|
+
if 'from_ref' in processed:
|
|
531
|
+
processed['from_ref'] = self._ensure_ref_prefix(
|
|
532
|
+
processed['from_ref']
|
|
533
|
+
)
|
|
534
|
+
if 'to_ref' in processed:
|
|
535
|
+
processed['to_ref'] = self._ensure_ref_prefix(processed['to_ref'])
|
|
536
|
+
|
|
537
|
+
# Handle inputs array for type_multiple
|
|
538
|
+
if 'inputs' in processed and isinstance(processed['inputs'], list):
|
|
539
|
+
processed_inputs = []
|
|
540
|
+
for input_item in processed['inputs']:
|
|
541
|
+
if isinstance(input_item, dict) and 'ref' in input_item:
|
|
542
|
+
processed_input = input_item.copy()
|
|
543
|
+
processed_input['ref'] = self._ensure_ref_prefix(
|
|
544
|
+
input_item['ref']
|
|
545
|
+
)
|
|
546
|
+
processed_inputs.append(processed_input)
|
|
547
|
+
else:
|
|
548
|
+
processed_inputs.append(input_item)
|
|
549
|
+
processed['inputs'] = processed_inputs
|
|
550
|
+
|
|
551
|
+
return processed
|
|
552
|
+
|
|
506
553
|
@action_logger
|
|
507
554
|
async def click(self, ref: str) -> Dict[str, Any]:
|
|
508
555
|
"""Click an element."""
|
|
@@ -515,6 +562,14 @@ class WebSocketBrowserWrapper:
|
|
|
515
562
|
response = await self._send_command('type', {'ref': ref, 'text': text})
|
|
516
563
|
return response
|
|
517
564
|
|
|
565
|
+
@action_logger
|
|
566
|
+
async def type_multiple(
|
|
567
|
+
self, inputs: List[Dict[str, str]]
|
|
568
|
+
) -> Dict[str, Any]:
|
|
569
|
+
"""Type text into multiple elements."""
|
|
570
|
+
response = await self._send_command('type', {'inputs': inputs})
|
|
571
|
+
return response
|
|
572
|
+
|
|
518
573
|
@action_logger
|
|
519
574
|
async def select(self, ref: str, value: str) -> Dict[str, Any]:
|
|
520
575
|
"""Select an option."""
|
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
import base64
|
|
16
16
|
import os
|
|
17
|
-
import uuid
|
|
18
17
|
from io import BytesIO
|
|
19
|
-
from typing import List, Literal, Optional
|
|
18
|
+
from typing import List, Literal, Optional, Union
|
|
20
19
|
|
|
21
20
|
from openai import OpenAI
|
|
22
21
|
from PIL import Image
|
|
@@ -64,13 +63,15 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
64
63
|
Literal["auto", "low", "medium", "high", "standard", "hd"]
|
|
65
64
|
] = "standard",
|
|
66
65
|
response_format: Optional[Literal["url", "b64_json"]] = "b64_json",
|
|
67
|
-
n: Optional[int] = 1,
|
|
68
66
|
background: Optional[
|
|
69
67
|
Literal["transparent", "opaque", "auto"]
|
|
70
68
|
] = "auto",
|
|
71
69
|
style: Optional[Literal["vivid", "natural"]] = None,
|
|
72
70
|
working_directory: Optional[str] = "image_save",
|
|
73
71
|
):
|
|
72
|
+
# NOTE: Some arguments are set in the constructor to prevent the agent
|
|
73
|
+
# from making invalid API calls with model-specific parameters. For
|
|
74
|
+
# example, the 'style' argument is only supported by 'dall-e-3'.
|
|
74
75
|
r"""Initializes a new instance of the OpenAIImageToolkit class.
|
|
75
76
|
|
|
76
77
|
Args:
|
|
@@ -94,8 +95,6 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
94
95
|
(default: :obj:`"standard"`)
|
|
95
96
|
response_format (Optional[Literal["url", "b64_json"]]):
|
|
96
97
|
The format of the response.(default: :obj:`"b64_json"`)
|
|
97
|
-
n (Optional[int]): The number of images to generate.
|
|
98
|
-
(default: :obj:`1`)
|
|
99
98
|
background (Optional[Literal["transparent", "opaque", "auto"]]):
|
|
100
99
|
The background of the image.(default: :obj:`"auto"`)
|
|
101
100
|
style (Optional[Literal["vivid", "natural"]]): The style of the
|
|
@@ -111,7 +110,6 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
111
110
|
self.size = size
|
|
112
111
|
self.quality = quality
|
|
113
112
|
self.response_format = response_format
|
|
114
|
-
self.n = n
|
|
115
113
|
self.background = background
|
|
116
114
|
self.style = style
|
|
117
115
|
self.working_directory: str = working_directory or "image_save"
|
|
@@ -140,11 +138,12 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
140
138
|
)
|
|
141
139
|
return None
|
|
142
140
|
|
|
143
|
-
def _build_base_params(self, prompt: str) -> dict:
|
|
141
|
+
def _build_base_params(self, prompt: str, n: Optional[int] = None) -> dict:
|
|
144
142
|
r"""Build base parameters dict for OpenAI API calls.
|
|
145
143
|
|
|
146
144
|
Args:
|
|
147
145
|
prompt (str): The text prompt for the image operation.
|
|
146
|
+
n (Optional[int]): The number of images to generate.
|
|
148
147
|
|
|
149
148
|
Returns:
|
|
150
149
|
dict: Parameters dictionary with non-None values.
|
|
@@ -152,8 +151,8 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
152
151
|
params = {"prompt": prompt, "model": self.model}
|
|
153
152
|
|
|
154
153
|
# basic parameters supported by all models
|
|
155
|
-
if
|
|
156
|
-
params["n"] =
|
|
154
|
+
if n is not None:
|
|
155
|
+
params["n"] = n # type: ignore[assignment]
|
|
157
156
|
if self.size is not None:
|
|
158
157
|
params["size"] = self.size
|
|
159
158
|
|
|
@@ -184,13 +183,16 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
184
183
|
return params
|
|
185
184
|
|
|
186
185
|
def _handle_api_response(
|
|
187
|
-
self, response, image_name: str, operation: str
|
|
186
|
+
self, response, image_name: Union[str, List[str]], operation: str
|
|
188
187
|
) -> str:
|
|
189
188
|
r"""Handle API response from OpenAI image operations.
|
|
190
189
|
|
|
191
190
|
Args:
|
|
192
191
|
response: The response object from OpenAI API.
|
|
193
|
-
image_name (str): Name for the saved image
|
|
192
|
+
image_name (Union[str, List[str]]): Name(s) for the saved image
|
|
193
|
+
file(s). If str, the same name is used for all images (will
|
|
194
|
+
cause error for multiple images). If list, must have exactly
|
|
195
|
+
the same length as the number of images generated.
|
|
194
196
|
operation (str): Operation type for success message ("generated").
|
|
195
197
|
|
|
196
198
|
Returns:
|
|
@@ -201,6 +203,21 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
201
203
|
logger.error(error_msg)
|
|
202
204
|
return error_msg
|
|
203
205
|
|
|
206
|
+
# Validate image_name parameter
|
|
207
|
+
if isinstance(image_name, list):
|
|
208
|
+
if len(image_name) != len(response.data):
|
|
209
|
+
error_msg = (
|
|
210
|
+
f"Error: Number of image names"
|
|
211
|
+
f" ({len(image_name)}) does not match number of "
|
|
212
|
+
f"images generated({len(response.data)})"
|
|
213
|
+
)
|
|
214
|
+
logger.error(error_msg)
|
|
215
|
+
return error_msg
|
|
216
|
+
image_names = image_name
|
|
217
|
+
else:
|
|
218
|
+
# If string, use same name for all images
|
|
219
|
+
image_names = [image_name] * len(response.data)
|
|
220
|
+
|
|
204
221
|
results = []
|
|
205
222
|
|
|
206
223
|
for i, image_data in enumerate(response.data):
|
|
@@ -215,18 +232,27 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
215
232
|
image_bytes = base64.b64decode(image_b64)
|
|
216
233
|
os.makedirs(self.working_directory, exist_ok=True)
|
|
217
234
|
|
|
218
|
-
|
|
219
|
-
if len(response.data) > 1:
|
|
220
|
-
filename = f"{image_name}_{i+1}_{uuid.uuid4().hex}.png"
|
|
221
|
-
else:
|
|
222
|
-
filename = f"{image_name}_{uuid.uuid4().hex}.png"
|
|
235
|
+
filename = f"{image_names[i]}"
|
|
223
236
|
|
|
224
237
|
image_path = os.path.join(self.working_directory, filename)
|
|
225
238
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
239
|
+
# Check if file already exists
|
|
240
|
+
if os.path.exists(image_path):
|
|
241
|
+
error_msg = (
|
|
242
|
+
f"Error: File '{image_path}' already exists. "
|
|
243
|
+
"Please use a different image_name."
|
|
244
|
+
)
|
|
245
|
+
logger.error(error_msg)
|
|
246
|
+
return error_msg
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
with open(image_path, "wb") as f:
|
|
250
|
+
f.write(image_bytes)
|
|
251
|
+
results.append(f"Image saved to {image_path}")
|
|
252
|
+
except Exception as e:
|
|
253
|
+
error_msg = f"Error saving image to '{image_path}': {e!s}"
|
|
254
|
+
logger.error(error_msg)
|
|
255
|
+
return error_msg
|
|
230
256
|
else:
|
|
231
257
|
error_msg = (
|
|
232
258
|
f"No valid image data (URL or base64) found in image {i+1}"
|
|
@@ -254,7 +280,8 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
254
280
|
def generate_image(
|
|
255
281
|
self,
|
|
256
282
|
prompt: str,
|
|
257
|
-
image_name: str = "image",
|
|
283
|
+
image_name: Union[str, List[str]] = "image.png",
|
|
284
|
+
n: int = 1,
|
|
258
285
|
) -> str:
|
|
259
286
|
r"""Generate an image using OpenAI's Image Generation models.
|
|
260
287
|
The generated image will be saved locally (for ``b64_json`` response
|
|
@@ -263,14 +290,18 @@ class OpenAIImageToolkit(BaseToolkit):
|
|
|
263
290
|
|
|
264
291
|
Args:
|
|
265
292
|
prompt (str): The text prompt to generate the image.
|
|
266
|
-
image_name (str): The name of the image to
|
|
267
|
-
|
|
293
|
+
image_name (Union[str, List[str]]): The name(s) of the image(s) to
|
|
294
|
+
save. The image name must end with `.png`. If str: same name
|
|
295
|
+
used for all images (causes error if n > 1). If list: must
|
|
296
|
+
match the number of images being generated (n parameter).
|
|
297
|
+
(default: :obj:`"image.png"`)
|
|
298
|
+
n (int): The number of images to generate. (default: :obj:`1`)
|
|
268
299
|
|
|
269
300
|
Returns:
|
|
270
301
|
str: the content of the model response or format of the response.
|
|
271
302
|
"""
|
|
272
303
|
try:
|
|
273
|
-
params = self._build_base_params(prompt)
|
|
304
|
+
params = self._build_base_params(prompt, n)
|
|
274
305
|
response = self.client.images.generate(**params)
|
|
275
306
|
return self._handle_api_response(response, image_name, "generated")
|
|
276
307
|
except Exception as e:
|
camel/types/enums.py
CHANGED
|
@@ -213,8 +213,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
213
213
|
MISTRAL_MIXTRAL_8x22B = "open-mixtral-8x22b"
|
|
214
214
|
MISTRAL_NEMO = "open-mistral-nemo"
|
|
215
215
|
MISTRAL_PIXTRAL_12B = "pixtral-12b-2409"
|
|
216
|
-
|
|
217
|
-
MAGISTRAL_MEDIUM = "magistral-medium-2506"
|
|
216
|
+
MISTRAL_MEDIUM_3_1 = "mistral-medium-2508"
|
|
218
217
|
MISTRAL_SMALL_3_2 = "mistral-small-2506"
|
|
219
218
|
|
|
220
219
|
# Reka models
|
|
@@ -663,8 +662,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
663
662
|
ModelType.MISTRAL_PIXTRAL_12B,
|
|
664
663
|
ModelType.MISTRAL_8B,
|
|
665
664
|
ModelType.MISTRAL_3B,
|
|
666
|
-
ModelType.
|
|
667
|
-
ModelType.MAGISTRAL_MEDIUM,
|
|
665
|
+
ModelType.MISTRAL_MEDIUM_3_1,
|
|
668
666
|
ModelType.MISTRAL_SMALL_3_2,
|
|
669
667
|
}
|
|
670
668
|
|
|
@@ -1225,7 +1223,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
1225
1223
|
ModelType.NETMIND_DEEPSEEK_R1,
|
|
1226
1224
|
ModelType.NETMIND_DEEPSEEK_V3,
|
|
1227
1225
|
ModelType.NOVITA_DEEPSEEK_V3_0324,
|
|
1228
|
-
ModelType.
|
|
1226
|
+
ModelType.MISTRAL_MEDIUM_3_1,
|
|
1229
1227
|
ModelType.ERNIE_4_5_TURBO_128K,
|
|
1230
1228
|
ModelType.DEEPSEEK_V3,
|
|
1231
1229
|
ModelType.MOONSHOT_KIMI_K2,
|
|
@@ -1336,10 +1334,6 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
1336
1334
|
ModelType.TOGETHER_LLAMA_4_SCOUT,
|
|
1337
1335
|
}:
|
|
1338
1336
|
return 10_000_000
|
|
1339
|
-
elif self in {
|
|
1340
|
-
ModelType.MAGISTRAL_MEDIUM,
|
|
1341
|
-
}:
|
|
1342
|
-
return 40_000
|
|
1343
1337
|
|
|
1344
1338
|
else:
|
|
1345
1339
|
logger.warning(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: camel-ai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.75a5
|
|
4
4
|
Summary: Communicative Agents for AI Society Study
|
|
5
5
|
Project-URL: Homepage, https://www.camel-ai.org/
|
|
6
6
|
Project-URL: Repository, https://github.com/camel-ai/camel
|
|
@@ -91,6 +91,7 @@ Requires-Dist: playwright>=1.50.0; extra == 'all'
|
|
|
91
91
|
Requires-Dist: prance<24,>=23.6.21.0; extra == 'all'
|
|
92
92
|
Requires-Dist: praw<8,>=7.7.1; extra == 'all'
|
|
93
93
|
Requires-Dist: pre-commit<4,>=3; extra == 'all'
|
|
94
|
+
Requires-Dist: protobuf>=6.0.0; extra == 'all'
|
|
94
95
|
Requires-Dist: psycopg[binary]<4,>=3.1.18; extra == 'all'
|
|
95
96
|
Requires-Dist: pyautogui<0.10,>=0.9.54; extra == 'all'
|
|
96
97
|
Requires-Dist: pydub<0.26,>=0.25.1; extra == 'all'
|
|
@@ -128,7 +129,6 @@ Requires-Dist: sympy<2,>=1.13.3; extra == 'all'
|
|
|
128
129
|
Requires-Dist: tabulate>=0.9.0; extra == 'all'
|
|
129
130
|
Requires-Dist: tavily-python<0.6,>=0.5.0; extra == 'all'
|
|
130
131
|
Requires-Dist: textblob<0.18,>=0.17.1; extra == 'all'
|
|
131
|
-
Requires-Dist: traceroot==0.0.4a5; extra == 'all'
|
|
132
132
|
Requires-Dist: transformers<5,>=4; extra == 'all'
|
|
133
133
|
Requires-Dist: tree-sitter-python<0.24,>=0.23.6; extra == 'all'
|
|
134
134
|
Requires-Dist: tree-sitter<0.24,>=0.23.2; extra == 'all'
|
|
@@ -193,7 +193,6 @@ Requires-Dist: ipykernel<7,>=6.0.0; extra == 'dev-tools'
|
|
|
193
193
|
Requires-Dist: jupyter-client<9,>=8.6.2; extra == 'dev-tools'
|
|
194
194
|
Requires-Dist: langfuse>=2.60.5; extra == 'dev-tools'
|
|
195
195
|
Requires-Dist: mcp>=1.3.0; extra == 'dev-tools'
|
|
196
|
-
Requires-Dist: traceroot==0.0.4a5; extra == 'dev-tools'
|
|
197
196
|
Requires-Dist: tree-sitter-python<0.24,>=0.23.6; extra == 'dev-tools'
|
|
198
197
|
Requires-Dist: tree-sitter<0.24,>=0.23.2; extra == 'dev-tools'
|
|
199
198
|
Requires-Dist: typer>=0.15.2; extra == 'dev-tools'
|
|
@@ -345,6 +344,7 @@ Requires-Dist: nebula3-python==3.8.2; extra == 'rag'
|
|
|
345
344
|
Requires-Dist: neo4j<6,>=5.18.0; extra == 'rag'
|
|
346
345
|
Requires-Dist: numpy<=2.2,>=1.2; extra == 'rag'
|
|
347
346
|
Requires-Dist: pandasai<3,>=2.3.0; extra == 'rag'
|
|
347
|
+
Requires-Dist: protobuf>=6.0.0; extra == 'rag'
|
|
348
348
|
Requires-Dist: pymilvus<3,>=2.4.0; extra == 'rag'
|
|
349
349
|
Requires-Dist: pyobvector>=0.1.18; extra == 'rag'
|
|
350
350
|
Requires-Dist: pytidb-experimental==0.0.1.dev4; extra == 'rag'
|
|
@@ -366,6 +366,7 @@ Requires-Dist: mem0ai>=0.1.73; extra == 'storage'
|
|
|
366
366
|
Requires-Dist: nebula3-python==3.8.2; extra == 'storage'
|
|
367
367
|
Requires-Dist: neo4j<6,>=5.18.0; extra == 'storage'
|
|
368
368
|
Requires-Dist: pgvector<0.3,>=0.2.4; extra == 'storage'
|
|
369
|
+
Requires-Dist: protobuf>=6.0.0; extra == 'storage'
|
|
369
370
|
Requires-Dist: psycopg[binary]<4,>=3.1.18; extra == 'storage'
|
|
370
371
|
Requires-Dist: pymilvus<3,>=2.4.0; extra == 'storage'
|
|
371
372
|
Requires-Dist: pyobvector>=0.1.18; extra == 'storage'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
camel/__init__.py,sha256=
|
|
1
|
+
camel/__init__.py,sha256=wTQgonEYCTyRP1M-YWHFakaFzw2aGRPRjVe8PVbM284,901
|
|
2
2
|
camel/generators.py,sha256=JRqj9_m1PF4qT6UtybzTQ-KBT9MJQt18OAAYvQ_fr2o,13844
|
|
3
3
|
camel/human.py,sha256=Xg8x1cS5KK4bQ1SDByiHZnzsRpvRP-KZViNvmu38xo4,5475
|
|
4
4
|
camel/logger.py,sha256=WgEwael_eT6D-lVAKHpKIpwXSTjvLbny5jbV1Ab8lnA,5760
|
|
@@ -358,7 +358,7 @@ camel/toolkits/note_taking_toolkit.py,sha256=FWnkKdPyTENzHDPSFKo9zOrhhONfJkFpRvn
|
|
|
358
358
|
camel/toolkits/notion_mcp_toolkit.py,sha256=ie_6Z-7DqDhgTiwYX8L3X47rfWGwzgwQH_s2DaK1ckc,8362
|
|
359
359
|
camel/toolkits/notion_toolkit.py,sha256=jmmVWk_WazRNWnx4r9DAvhFTAL-n_ige0tb32UHJ_ik,9752
|
|
360
360
|
camel/toolkits/open_api_toolkit.py,sha256=Venfq8JwTMQfzRzzB7AYmYUMEX35hW0BjIv_ozFMiNk,23316
|
|
361
|
-
camel/toolkits/openai_image_toolkit.py,sha256=
|
|
361
|
+
camel/toolkits/openai_image_toolkit.py,sha256=6Hcadut2cwJrgh5RObtErYNzsl93gC28NoJTS-HvYw8,12930
|
|
362
362
|
camel/toolkits/openbb_toolkit.py,sha256=8yBZL9E2iSgskosBQhD3pTP56oV6gerWpFjIJc_2UMo,28935
|
|
363
363
|
camel/toolkits/origene_mcp_toolkit.py,sha256=og3H-F5kWRRIyOyhF7BR-ig_JIpZPKE0DCA1QJZw5BY,3354
|
|
364
364
|
camel/toolkits/page_script.js,sha256=mXepZPnQNVLp_Wgb64lv7DMQIJYl_XIHJHLVt1OFZO4,13146
|
|
@@ -390,18 +390,18 @@ camel/toolkits/zapier_toolkit.py,sha256=A83y1UcfuopH7Fx82pORzypl1StbhBjB2HhyOqYa
|
|
|
390
390
|
camel/toolkits/hybrid_browser_toolkit/__init__.py,sha256=vxjWhq7GjUKE5I9RGQU_GoikZJ-AVK4ertdvEqp9pd0,802
|
|
391
391
|
camel/toolkits/hybrid_browser_toolkit/config_loader.py,sha256=yUmbmt-TWLbCyxGa63lVJ47KRdZsEJI6-2y3x0SfdBM,7040
|
|
392
392
|
camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py,sha256=gotOOlXJjfjv9Qnn89PLNhJ4_Rw_aMMU6gTJcG-uCf8,7938
|
|
393
|
-
camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py,sha256=
|
|
394
|
-
camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py,sha256=
|
|
393
|
+
camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py,sha256=f1m0cBvQCM5XlgPKWLSUGAlTqRqlI1ZKYs3KcrLqJ-w,54919
|
|
394
|
+
camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py,sha256=Cx3DpZctlwNlSoNa4sa3DOky_nPUKId98jCq63uXNgc,23903
|
|
395
395
|
camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json,sha256=_-YE9S_C1XT59A6upQp9lLuZcC67cV9QlbwAsEKkfyw,156337
|
|
396
396
|
camel/toolkits/hybrid_browser_toolkit/ts/package.json,sha256=pUQm0xwXR7ZyWNv6O2QtHW00agnfAoX9F_XGXZlAxl4,745
|
|
397
397
|
camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json,sha256=SwpQnq4Q-rwRobF2iWrP96mgmgwaVPZEv-nii5QIYEU,523
|
|
398
|
-
camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js,sha256=
|
|
398
|
+
camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js,sha256=NeBhFUQl6dOyDeeh0c8LZ7f3qZssMHIrqJSP1mZEeiE,9672
|
|
399
399
|
camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js,sha256=NNwM_H2xaDrlrdac0PJK1iUBwdiuQsg9qKaMhHAvZuI,3160
|
|
400
|
-
camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts,sha256=
|
|
401
|
-
camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts,sha256=
|
|
402
|
-
camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts,sha256=
|
|
400
|
+
camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts,sha256=fcqZoTJArx9UTBzjelZkX7zse5HwbqqwFjQeMbeRWQI,42893
|
|
401
|
+
camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts,sha256=36o-N3YNTtLxdp5MfGHj4h7LowCx0o3oxKZKFBoFchA,6218
|
|
402
|
+
camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts,sha256=oz5rs3u7vqWxssCYNWkqJu4JOmk7dRbf2SMIEputHEs,22149
|
|
403
403
|
camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts,sha256=uJGHmGs640iCrjllqXDXwDE4hGW1VJA2YL6BkFkzYNs,353
|
|
404
|
-
camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts,sha256=
|
|
404
|
+
camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts,sha256=NFOJKROXYDHSXxcr15LHm36rPfRpVS2hsIobXcZgTRU,2964
|
|
405
405
|
camel/toolkits/hybrid_browser_toolkit_py/__init__.py,sha256=tPQloCw-Ayl1lPfyvzGJkMFa1ze3ilXJq_1Oz5jg5s4,796
|
|
406
406
|
camel/toolkits/hybrid_browser_toolkit_py/actions.py,sha256=S77VLL9hOq5M2RdPQrIAgjxinwS2ETd6kH6m6vtub5o,20935
|
|
407
407
|
camel/toolkits/hybrid_browser_toolkit_py/agent.py,sha256=0ifwhYUDJ5GLxfdpC5KquPaW1a0QBAutp2Y9y0YFujw,11685
|
|
@@ -437,7 +437,7 @@ camel/toolkits/open_api_specs/web_scraper/openapi.yaml,sha256=u_WalQ01e8W1D27VnZ
|
|
|
437
437
|
camel/toolkits/open_api_specs/web_scraper/paths/__init__.py,sha256=OKCZrQCDwaWtXIN_2rA9FSqEvgpQRieRoHh7Ek6N16A,702
|
|
438
438
|
camel/toolkits/open_api_specs/web_scraper/paths/scraper.py,sha256=aWy1_ppV4NVVEZfnbN3tu9XA9yAPAC9bRStJ5JuXMRU,1117
|
|
439
439
|
camel/types/__init__.py,sha256=EOmWlqS7aE5cB51_Vv7vHUexKeBbx9FSsfynl5vKjwo,2565
|
|
440
|
-
camel/types/enums.py,sha256=
|
|
440
|
+
camel/types/enums.py,sha256=x76UYX53jaUF0-qgUa0KFwRufo8cekb4-QGirlfTKB0,63781
|
|
441
441
|
camel/types/mcp_registries.py,sha256=dl4LgYtSaUhsqAKpz28k_SA9La11qxqBvDLaEuyzrFE,4971
|
|
442
442
|
camel/types/openai_types.py,sha256=m7oWb8nWYWOAwBRY1mP9mS9RVufXeDVj-fGvHAhXuMU,2120
|
|
443
443
|
camel/types/unified_model_type.py,sha256=U3NUZux7QuMIxPW2H0qDp9BOyDJFHAx6jUmDNw5_9KM,5912
|
|
@@ -466,7 +466,7 @@ camel/verifiers/math_verifier.py,sha256=tA1D4S0sm8nsWISevxSN0hvSVtIUpqmJhzqfbuMo
|
|
|
466
466
|
camel/verifiers/models.py,sha256=GdxYPr7UxNrR1577yW4kyroRcLGfd-H1GXgv8potDWU,2471
|
|
467
467
|
camel/verifiers/physics_verifier.py,sha256=c1grrRddcrVN7szkxhv2QirwY9viIRSITWeWFF5HmLs,30187
|
|
468
468
|
camel/verifiers/python_verifier.py,sha256=ogTz77wODfEcDN4tMVtiSkRQyoiZbHPY2fKybn59lHw,20558
|
|
469
|
-
camel_ai-0.2.
|
|
470
|
-
camel_ai-0.2.
|
|
471
|
-
camel_ai-0.2.
|
|
472
|
-
camel_ai-0.2.
|
|
469
|
+
camel_ai-0.2.75a5.dist-info/METADATA,sha256=R6EVTTS1N9_Tr6y_hwN4hC0ppiWl8z6XrXcmZEGmqcc,52328
|
|
470
|
+
camel_ai-0.2.75a5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
471
|
+
camel_ai-0.2.75a5.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
|
|
472
|
+
camel_ai-0.2.75a5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|