camel-ai 0.2.73a3__py3-none-any.whl → 0.2.73a4__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.
@@ -79,7 +79,9 @@ class WebDeployToolkit(BaseToolkit):
79
79
  self.tag_text = self._sanitize_text(tag_text)
80
80
  self.tag_url = self._validate_url(tag_url)
81
81
  self.remote_server_ip = (
82
- self._validate_ip(remote_server_ip) if remote_server_ip else None
82
+ self._validate_ip_or_domain(remote_server_ip)
83
+ if remote_server_ip
84
+ else None
83
85
  )
84
86
  self.remote_server_port = self._validate_port(remote_server_port)
85
87
  self.server_registry_file = os.path.join(
@@ -87,24 +89,36 @@ class WebDeployToolkit(BaseToolkit):
87
89
  )
88
90
  self._load_server_registry()
89
91
 
90
- def _validate_ip(self, ip: str) -> str:
91
- """Validate IP address format."""
92
+ def _validate_ip_or_domain(self, address: str) -> str:
93
+ r"""Validate IP address or domain name format."""
92
94
  import ipaddress
95
+ import re
93
96
 
94
97
  try:
95
- ipaddress.ip_address(ip)
96
- return ip
98
+ # Try to validate as IP address first
99
+ ipaddress.ip_address(address)
100
+ return address
97
101
  except ValueError:
98
- raise ValueError(f"Invalid IP address: {ip}")
102
+ # If not a valid IP, check if it's a valid domain name
103
+ domain_pattern = re.compile(
104
+ r'^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?'
105
+ r'(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$'
106
+ )
107
+ if domain_pattern.match(address) and len(address) <= 253:
108
+ return address
109
+ else:
110
+ raise ValueError(
111
+ f"Invalid IP address or domain name: {address}"
112
+ )
99
113
 
100
114
  def _validate_port(self, port: int) -> int:
101
- """Validate port number."""
115
+ r"""Validate port number."""
102
116
  if not isinstance(port, int) or port < 1 or port > 65535:
103
117
  raise ValueError(f"Invalid port number: {port}")
104
118
  return port
105
119
 
106
120
  def _sanitize_text(self, text: str) -> str:
107
- """Sanitize text to prevent XSS."""
121
+ r"""Sanitize text to prevent XSS."""
108
122
  if not isinstance(text, str):
109
123
  return ""
110
124
  # Remove any HTML/script tags
@@ -119,7 +133,7 @@ class WebDeployToolkit(BaseToolkit):
119
133
  return text[:100] # Limit length
120
134
 
121
135
  def _validate_url(self, url: str) -> str:
122
- """Validate URL format."""
136
+ r"""Validate URL format."""
123
137
  if not isinstance(url, str):
124
138
  raise ValueError("URL must be a string")
125
139
  # Basic URL validation
@@ -139,7 +153,7 @@ class WebDeployToolkit(BaseToolkit):
139
153
  def _validate_subdirectory(
140
154
  self, subdirectory: Optional[str]
141
155
  ) -> Optional[str]:
142
- """Validate subdirectory to prevent path traversal."""
156
+ r"""Validate subdirectory to prevent path traversal."""
143
157
  if subdirectory is None:
144
158
  return None
145
159
 
@@ -157,7 +171,7 @@ class WebDeployToolkit(BaseToolkit):
157
171
  return subdirectory
158
172
 
159
173
  def _is_port_available(self, port: int) -> bool:
160
- """Check if a port is available for binding."""
174
+ r"""Check if a port is available for binding."""
161
175
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
162
176
  try:
163
177
  sock.bind(('127.0.0.1', port))
@@ -604,7 +618,7 @@ class WebDeployToolkit(BaseToolkit):
604
618
  return {
605
619
  'success': False,
606
620
  'error': (
607
- f'Port {port} is already in use by ' f'another process'
621
+ f'Port {port} is already in use by another process'
608
622
  ),
609
623
  }
610
624
 
@@ -702,6 +716,44 @@ class WebDeployToolkit(BaseToolkit):
702
716
  # Validate subdirectory
703
717
  subdirectory = self._validate_subdirectory(subdirectory)
704
718
 
719
+ # Check if remote deployment is configured
720
+ if self.remote_server_ip:
721
+ return self._deploy_folder_to_remote_server(
722
+ folder_path,
723
+ subdirectory,
724
+ domain,
725
+ )
726
+ else:
727
+ return self._deploy_folder_to_local_server(
728
+ folder_path,
729
+ port,
730
+ domain,
731
+ subdirectory,
732
+ )
733
+
734
+ except Exception as e:
735
+ logger.error(f"Error deploying folder: {e}")
736
+ return {'success': False, 'error': str(e)}
737
+
738
+ def _deploy_folder_to_local_server(
739
+ self,
740
+ folder_path: str,
741
+ port: int,
742
+ domain: Optional[str],
743
+ subdirectory: Optional[str],
744
+ ) -> Dict[str, Any]:
745
+ r"""Deploy folder to local server (original functionality).
746
+
747
+ Args:
748
+ folder_path (str): Path to the folder to deploy
749
+ port (int): Port to serve on
750
+ domain (Optional[str]): Custom domain
751
+ subdirectory (Optional[str]): Subdirectory path
752
+
753
+ Returns:
754
+ Dict[str, Any]: Deployment result
755
+ """
756
+ try:
705
757
  temp_dir = None
706
758
  if self.add_branding_tag:
707
759
  # Create temporary directory and copy all files
@@ -877,6 +929,149 @@ class WebDeployToolkit(BaseToolkit):
877
929
  logger.error(f"Error deploying folder: {e}")
878
930
  return {'success': False, 'error': str(e)}
879
931
 
932
+ def _deploy_folder_to_remote_server(
933
+ self,
934
+ folder_path: str,
935
+ subdirectory: Optional[str] = None,
936
+ domain: Optional[str] = None,
937
+ ) -> Dict[str, Any]:
938
+ r"""Deploy folder to remote server via API.
939
+
940
+ Args:
941
+ folder_path (str): Path to the folder to deploy
942
+ subdirectory (Optional[str]): Subdirectory path for deployment
943
+ domain (Optional[str]): Custom domain
944
+
945
+ Returns:
946
+ Dict[str, Any]: Deployment result
947
+ """
948
+ try:
949
+ import tempfile
950
+ import zipfile
951
+
952
+ import requests
953
+
954
+ # Validate subdirectory
955
+ subdirectory = self._validate_subdirectory(subdirectory)
956
+
957
+ # Create a temporary zip file of the folder
958
+ with tempfile.NamedTemporaryFile(
959
+ suffix='.zip', delete=False
960
+ ) as temp_zip:
961
+ zip_path = temp_zip.name
962
+
963
+ try:
964
+ # Create zip archive
965
+ with zipfile.ZipFile(
966
+ zip_path, 'w', zipfile.ZIP_DEFLATED
967
+ ) as zipf:
968
+ for root, _, files in os.walk(folder_path):
969
+ for file in files:
970
+ file_path = os.path.join(root, file)
971
+ # Calculate relative path within the archive
972
+ arcname = os.path.relpath(file_path, folder_path)
973
+ zipf.write(file_path, arcname)
974
+
975
+ # Read zip file as base64
976
+ with open(zip_path, 'rb') as f:
977
+ zip_data = base64.b64encode(f.read()).decode('utf-8')
978
+
979
+ # Prepare deployment data
980
+ deploy_data = {
981
+ "deployment_type": "folder",
982
+ "folder_data": zip_data,
983
+ "subdirectory": subdirectory,
984
+ "domain": domain,
985
+ "timestamp": time.time(),
986
+ }
987
+
988
+ # Add logo data if custom logo is specified
989
+ if self.logo_path and os.path.exists(self.logo_path):
990
+ try:
991
+ logo_ext = os.path.splitext(self.logo_path)[1]
992
+ logo_filename = f"custom_logo{logo_ext}"
993
+
994
+ with open(self.logo_path, 'rb') as logo_file:
995
+ logo_data = base64.b64encode(
996
+ logo_file.read()
997
+ ).decode('utf-8')
998
+
999
+ deploy_data.update(
1000
+ {
1001
+ "logo_data": logo_data,
1002
+ "logo_ext": logo_ext,
1003
+ "logo_filename": logo_filename,
1004
+ }
1005
+ )
1006
+ except Exception as logo_error:
1007
+ logger.warning(
1008
+ f"Failed to process custom logo: {logo_error}"
1009
+ )
1010
+
1011
+ # Send to remote server API
1012
+ api_url = f"http://{self.remote_server_ip}:{self.remote_server_port}/api/deploy"
1013
+
1014
+ response = requests.post(
1015
+ api_url,
1016
+ json=deploy_data,
1017
+ timeout=self.timeout
1018
+ or 60, # Extended timeout for folder uploads
1019
+ allow_redirects=False,
1020
+ headers={'Content-Type': 'application/json'},
1021
+ )
1022
+
1023
+ if response.status_code == 200:
1024
+ result = response.json()
1025
+
1026
+ # Build URLs
1027
+ base_url = f"http://{self.remote_server_ip}:{self.remote_server_port}"
1028
+ deployed_url = (
1029
+ f"{base_url}/{subdirectory}/"
1030
+ if subdirectory
1031
+ else base_url
1032
+ )
1033
+
1034
+ return {
1035
+ 'success': True,
1036
+ 'remote_url': deployed_url,
1037
+ 'server_ip': self.remote_server_ip,
1038
+ 'subdirectory': subdirectory,
1039
+ 'domain': domain,
1040
+ 'message': (
1041
+ f'Successfully deployed folder to remote server!\n'
1042
+ f' • Access URL: {deployed_url}\n'
1043
+ f' • Server: '
1044
+ f'{self.remote_server_ip}:{self.remote_server_port}'
1045
+ ),
1046
+ 'branding_tag_added': self.add_branding_tag,
1047
+ 'logo_processed': result.get('logo_processed', False),
1048
+ }
1049
+ else:
1050
+ return {
1051
+ 'success': False,
1052
+ 'error': (
1053
+ f'Remote folder deployment failed: '
1054
+ f'HTTP {response.status_code}'
1055
+ ),
1056
+ }
1057
+
1058
+ finally:
1059
+ # Clean up temporary zip file
1060
+ if os.path.exists(zip_path):
1061
+ os.unlink(zip_path)
1062
+
1063
+ except ImportError:
1064
+ return {
1065
+ 'success': False,
1066
+ 'error': 'Remote deployment requires requests library. '
1067
+ 'Install with: pip install requests',
1068
+ }
1069
+ except Exception as e:
1070
+ return {
1071
+ 'success': False,
1072
+ 'error': f'Remote folder deployment error: {e!s}',
1073
+ }
1074
+
880
1075
  def stop_server(self, port: int) -> Dict[str, Any]:
881
1076
  r"""Stop a running server on the specified port.
882
1077
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: camel-ai
3
- Version: 0.2.73a3
3
+ Version: 0.2.73a4
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
@@ -124,6 +124,7 @@ Requires-Dist: slack-bolt<2,>=1.20.1; extra == 'all'
124
124
  Requires-Dist: slack-sdk<4,>=3.27.2; extra == 'all'
125
125
  Requires-Dist: soundfile<0.14,>=0.13; extra == 'all'
126
126
  Requires-Dist: stripe<12,>=11.3.0; extra == 'all'
127
+ Requires-Dist: surrealdb>=1.0.6; extra == 'all'
127
128
  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'
@@ -336,6 +337,7 @@ Requires-Dist: pyobvector>=0.1.18; extra == 'storage'
336
337
  Requires-Dist: pytidb-experimental==0.0.1.dev4; extra == 'storage'
337
338
  Requires-Dist: qdrant-client<2,>=1.9.0; extra == 'storage'
338
339
  Requires-Dist: redis<6,>=5.0.6; extra == 'storage'
340
+ Requires-Dist: surrealdb>=1.0.6; extra == 'storage'
339
341
  Requires-Dist: weaviate-client>=4.15.0; extra == 'storage'
340
342
  Provides-Extra: web-tools
341
343
  Requires-Dist: apify-client<2,>=1.8.1; extra == 'web-tools'
@@ -1,4 +1,4 @@
1
- camel/__init__.py,sha256=LY1yvi28uSSDg8ifjgLeFiCqzATMOyd5fXqDmkOmEAo,901
1
+ camel/__init__.py,sha256=1A_s4813pCXFzfgs6rYH4XAW07sXn8m93RQ8jiUaKvQ,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
@@ -299,7 +299,7 @@ camel/storages/object_storages/amazon_s3.py,sha256=9Yvyyyb1LGHxr8REEza7oGopbVtLE
299
299
  camel/storages/object_storages/azure_blob.py,sha256=66dHcvjE2ZNdb339oBU6LbFiKzPYrnlb4tQ_3m2Yazc,5992
300
300
  camel/storages/object_storages/base.py,sha256=pImWylYJm7Wt8q87lBE1Cxk26IJ9sRtXq_OJmV6bJlg,3796
301
301
  camel/storages/object_storages/google_cloud.py,sha256=59AvGar_GDoGYHhzUi5KBtInv2KaUVnw8SalsL43410,5332
302
- camel/storages/vectordb_storages/__init__.py,sha256=RSVrBZrCHFhjhil0AKhNpH-eiNd2RmxazpnhAtpbhOc,1434
302
+ camel/storages/vectordb_storages/__init__.py,sha256=Nztyo6ZvEYJ7yWz6Id4ImCpTvGaMTM0v8wFM2dMjOBg,1470
303
303
  camel/storages/vectordb_storages/base.py,sha256=EP_WbEtI3SJPHro9rjNkIq9UDUP1AAHmxZgeya94Lgk,6738
304
304
  camel/storages/vectordb_storages/chroma.py,sha256=wXuLUYsgkC2VvdyLrlL5VqEDVzJDBUo7OdimK8hBLmg,27553
305
305
  camel/storages/vectordb_storages/faiss.py,sha256=MHE3db9kJmVuu0aScXsSo8p60TCtc2Ot0rO77zcPgt8,26760
@@ -307,7 +307,7 @@ camel/storages/vectordb_storages/milvus.py,sha256=ChQyEuaXCWCKxytLN2z4QrkEthx2xE
307
307
  camel/storages/vectordb_storages/oceanbase.py,sha256=eNBelw4D6r3OWlhHzGJ8Xw-ej9nU1uTZ6CYoXdbxDkI,17054
308
308
  camel/storages/vectordb_storages/pgvector.py,sha256=p-5RGCVT46zP-Yop85thWi2m0ZnHILSJFpu2A-7qWnk,12438
309
309
  camel/storages/vectordb_storages/qdrant.py,sha256=a_cT0buSCHQ2CPZy852-mdvMDwy5zodCvAKMaa4zIvg,18017
310
- camel/storages/vectordb_storages/surreal.py,sha256=QFg-Ogoo_pT8KkzZH0BmZpLCd5wQTfkuDD1A0zodq5s,15465
310
+ camel/storages/vectordb_storages/surreal.py,sha256=vPUh3iFz73kiDFP_FLDzq3ULaFGQGAcJDkOh9Y8SKps,13392
311
311
  camel/storages/vectordb_storages/tidb.py,sha256=w83bxgKgso43MtHqlpf2EMSpn1_Nz6ZZtY4fPw_-vgs,11192
312
312
  camel/storages/vectordb_storages/weaviate.py,sha256=wDUE4KvfmOl3DqHFU4uF0VKbHu-q9vKhZDe8FZ6QXsk,27888
313
313
  camel/tasks/__init__.py,sha256=MuHwkw5GRQc8NOCzj8tjtBrr2Xg9KrcKp-ed_-2ZGIM,906
@@ -384,7 +384,7 @@ camel/toolkits/twitter_toolkit.py,sha256=Px4N8aUxUzy01LhGSWkdrC2JgwKkrY3cvxgMeJ2
384
384
  camel/toolkits/video_analysis_toolkit.py,sha256=h6D7b1MAAzaHn222n_YKtwG-EGEGgMt7mBrNNVipYZc,23361
385
385
  camel/toolkits/video_download_toolkit.py,sha256=0dOzsG9vpCHr31bW4Iiqz4iMkQ7uxkAyTMgiculvenk,7567
386
386
  camel/toolkits/weather_toolkit.py,sha256=fs9x9aC38Wsvni6A4PPpbRX6-aBnZiqs2Jix39yoULU,7413
387
- camel/toolkits/web_deploy_toolkit.py,sha256=8dhbYmx5opmimDRdXwEU7LPeG8Z17TWUEURbEneyIPg,38847
387
+ camel/toolkits/web_deploy_toolkit.py,sha256=jUiU8ur759Q0Aiw5TUcvhu7Iv10W36Y1RS0hEHZIb4U,46154
388
388
  camel/toolkits/whatsapp_toolkit.py,sha256=udUQXkXyeWsmrUlOJZsGBhHtc_jhB05Axe_TchhibsU,5760
389
389
  camel/toolkits/wolfram_alpha_toolkit.py,sha256=qeIM8ySn5ilcExBWtx-hDOc35bNcebLVnZ67kt1H3mQ,9295
390
390
  camel/toolkits/zapier_toolkit.py,sha256=A83y1UcfuopH7Fx82pORzypl1StbhBjB2HhyOqYa300,7124
@@ -408,7 +408,7 @@ camel/toolkits/hybrid_browser_toolkit_py/actions.py,sha256=x6X-kEdQx3K1ID-BBwdQE
408
408
  camel/toolkits/hybrid_browser_toolkit_py/agent.py,sha256=0ifwhYUDJ5GLxfdpC5KquPaW1a0QBAutp2Y9y0YFujw,11685
409
409
  camel/toolkits/hybrid_browser_toolkit_py/browser_session.py,sha256=fgV1o4pWOQ_fUvmpk7UHYcJCqHY_cmivoY_OB0ZKv3s,26866
410
410
  camel/toolkits/hybrid_browser_toolkit_py/config_loader.py,sha256=0zpDq3aFKEva2jc38kgLHnyxypIDk9SOcMjoP26tozo,15414
411
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py,sha256=nph9Nyam4349zPPA6f2NZ7KPA-xNLW18qHqooBeVlFM,76798
411
+ camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py,sha256=z-v49Su2XTWCERJuv2AR3GeJHpv5Iad2fa3tenz40eQ,80190
412
412
  camel/toolkits/hybrid_browser_toolkit_py/snapshot.py,sha256=3vQaFK5C1P8WnkK2eDMaFOizrZf4uUAW0LxdeoF4F2w,8539
413
413
  camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js,sha256=z4XRSVUpAS87Sj36s3bY8XXhvRcHBlgsUOyMxV2HI80,27650
414
414
  camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js,sha256=VnzIn0KcEtxuncJi-OPZzdWbLSeSHCH-7sviFAGNM8g,40823
@@ -467,7 +467,7 @@ camel/verifiers/math_verifier.py,sha256=tA1D4S0sm8nsWISevxSN0hvSVtIUpqmJhzqfbuMo
467
467
  camel/verifiers/models.py,sha256=GdxYPr7UxNrR1577yW4kyroRcLGfd-H1GXgv8potDWU,2471
468
468
  camel/verifiers/physics_verifier.py,sha256=c1grrRddcrVN7szkxhv2QirwY9viIRSITWeWFF5HmLs,30187
469
469
  camel/verifiers/python_verifier.py,sha256=ogTz77wODfEcDN4tMVtiSkRQyoiZbHPY2fKybn59lHw,20558
470
- camel_ai-0.2.73a3.dist-info/METADATA,sha256=vy-EgY3IPDzVVc5UNfT7MQvrWqvpBZ1ZUPGQb-bXOrA,50334
471
- camel_ai-0.2.73a3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
472
- camel_ai-0.2.73a3.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
473
- camel_ai-0.2.73a3.dist-info/RECORD,,
470
+ camel_ai-0.2.73a4.dist-info/METADATA,sha256=eiu76i0Cyxv90DdcFdRBgWHY9ZlXP54ule0zp0VSsiA,50434
471
+ camel_ai-0.2.73a4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
472
+ camel_ai-0.2.73a4.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
473
+ camel_ai-0.2.73a4.dist-info/RECORD,,