camel-ai 0.2.73a5__py3-none-any.whl → 0.2.73a8__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/agents/chat_agent.py +1 -6
- camel/societies/__init__.py +2 -0
- camel/toolkits/mcp_toolkit.py +341 -46
- camel/toolkits/terminal_toolkit.py +201 -20
- {camel_ai-0.2.73a5.dist-info → camel_ai-0.2.73a8.dist-info}/METADATA +4 -4
- {camel_ai-0.2.73a5.dist-info → camel_ai-0.2.73a8.dist-info}/RECORD +9 -9
- {camel_ai-0.2.73a5.dist-info → camel_ai-0.2.73a8.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.73a5.dist-info → camel_ai-0.2.73a8.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
camel/agents/chat_agent.py
CHANGED
|
@@ -2798,12 +2798,7 @@ class ChatAgent(BaseAgent):
|
|
|
2798
2798
|
)
|
|
2799
2799
|
thread = threading.Thread(
|
|
2800
2800
|
target=tool_worker,
|
|
2801
|
-
args=(
|
|
2802
|
-
self._internal_tools[function_name],
|
|
2803
|
-
args,
|
|
2804
|
-
result_queue,
|
|
2805
|
-
tool_call_data,
|
|
2806
|
-
),
|
|
2801
|
+
args=(result_queue, tool_call_data),
|
|
2807
2802
|
)
|
|
2808
2803
|
thread.start()
|
|
2809
2804
|
|
camel/societies/__init__.py
CHANGED
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from .babyagi_playing import BabyAGI
|
|
15
15
|
from .role_playing import RolePlaying
|
|
16
|
+
from .workforce import Workforce
|
|
16
17
|
|
|
17
18
|
__all__ = [
|
|
18
19
|
'RolePlaying',
|
|
19
20
|
'BabyAGI',
|
|
21
|
+
'Workforce',
|
|
20
22
|
]
|
camel/toolkits/mcp_toolkit.py
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import json
|
|
16
16
|
import os
|
|
17
|
+
import warnings
|
|
17
18
|
from contextlib import AsyncExitStack
|
|
18
19
|
from typing import Any, Dict, List, Optional
|
|
19
20
|
|
|
@@ -24,6 +25,11 @@ from camel.utils.mcp_client import MCPClient, create_mcp_client
|
|
|
24
25
|
|
|
25
26
|
logger = get_logger(__name__)
|
|
26
27
|
|
|
28
|
+
# Suppress parameter description warnings for MCP tools
|
|
29
|
+
warnings.filterwarnings(
|
|
30
|
+
"ignore", message="Parameter description is missing", category=UserWarning
|
|
31
|
+
)
|
|
32
|
+
|
|
27
33
|
|
|
28
34
|
class MCPConnectionError(Exception):
|
|
29
35
|
r"""Raised when MCP connection fails."""
|
|
@@ -446,7 +452,7 @@ class MCPToolkit(BaseToolkit):
|
|
|
446
452
|
|
|
447
453
|
def _ensure_strict_tool_schema(self, tool: FunctionTool) -> FunctionTool:
|
|
448
454
|
r"""Ensure a tool has a strict schema compatible with OpenAI's
|
|
449
|
-
requirements.
|
|
455
|
+
requirements according to the structured outputs specification.
|
|
450
456
|
|
|
451
457
|
Args:
|
|
452
458
|
tool (FunctionTool): The tool to check and update if necessary.
|
|
@@ -457,60 +463,333 @@ class MCPToolkit(BaseToolkit):
|
|
|
457
463
|
try:
|
|
458
464
|
schema = tool.get_openai_tool_schema()
|
|
459
465
|
|
|
460
|
-
#
|
|
461
|
-
def
|
|
462
|
-
r"""Recursively
|
|
466
|
+
# Helper functions for validation and transformation
|
|
467
|
+
def _validate_and_fix_schema(obj, path="", in_root=True):
|
|
468
|
+
r"""Recursively validate and fix schema to meet strict
|
|
469
|
+
requirements.
|
|
470
|
+
"""
|
|
463
471
|
if isinstance(obj, dict):
|
|
464
|
-
if
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if
|
|
468
|
-
|
|
472
|
+
# Check if this is the root object
|
|
473
|
+
if in_root and path == "":
|
|
474
|
+
# Root must be an object, not anyOf
|
|
475
|
+
if "anyOf" in obj and "type" not in obj:
|
|
476
|
+
raise ValueError(
|
|
477
|
+
"Root object must not be anyOf and must "
|
|
478
|
+
"be an object"
|
|
479
|
+
)
|
|
480
|
+
if obj.get("type") and obj["type"] != "object":
|
|
481
|
+
raise ValueError(
|
|
482
|
+
"Root object must have type 'object'"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Handle object types
|
|
486
|
+
if obj.get("type") == "object":
|
|
487
|
+
# Ensure additionalProperties is false
|
|
488
|
+
obj["additionalProperties"] = False
|
|
489
|
+
|
|
490
|
+
# Process properties
|
|
491
|
+
if "properties" in obj:
|
|
492
|
+
props = obj["properties"]
|
|
493
|
+
# Only set required if it doesn't exist or needs
|
|
494
|
+
# updating
|
|
495
|
+
if "required" not in obj:
|
|
496
|
+
# If no required field exists, make all fields
|
|
497
|
+
# required
|
|
498
|
+
obj["required"] = list(props.keys())
|
|
499
|
+
else:
|
|
500
|
+
# Ensure required field only contains valid
|
|
501
|
+
# property names
|
|
502
|
+
existing_required = obj.get("required", [])
|
|
503
|
+
valid_required = [
|
|
504
|
+
req
|
|
505
|
+
for req in existing_required
|
|
506
|
+
if req in props
|
|
507
|
+
]
|
|
508
|
+
# Add any missing properties to required
|
|
509
|
+
for prop_name in props:
|
|
510
|
+
if prop_name not in valid_required:
|
|
511
|
+
valid_required.append(prop_name)
|
|
512
|
+
obj["required"] = valid_required
|
|
513
|
+
|
|
514
|
+
# Recursively process each property
|
|
515
|
+
for prop_name, prop_schema in props.items():
|
|
516
|
+
_validate_and_fix_schema(
|
|
517
|
+
prop_schema, f"{path}.{prop_name}", False
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
# Handle arrays
|
|
521
|
+
elif obj.get("type") == "array":
|
|
522
|
+
if "items" in obj:
|
|
523
|
+
_validate_and_fix_schema(
|
|
524
|
+
obj["items"], f"{path}.items", False
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
# Handle anyOf
|
|
528
|
+
elif "anyOf" in obj:
|
|
529
|
+
# Validate anyOf schemas
|
|
530
|
+
for i, schema in enumerate(obj["anyOf"]):
|
|
531
|
+
_validate_and_fix_schema(
|
|
532
|
+
schema, f"{path}.anyOf[{i}]", False
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
# Handle string format validation
|
|
536
|
+
elif obj.get("type") == "string":
|
|
537
|
+
if "format" in obj:
|
|
538
|
+
allowed_formats = [
|
|
539
|
+
"date-time",
|
|
540
|
+
"time",
|
|
541
|
+
"date",
|
|
542
|
+
"duration",
|
|
543
|
+
"email",
|
|
544
|
+
"hostname",
|
|
545
|
+
"ipv4",
|
|
546
|
+
"ipv6",
|
|
547
|
+
"uuid",
|
|
548
|
+
]
|
|
549
|
+
if obj["format"] not in allowed_formats:
|
|
550
|
+
del obj["format"] # Remove unsupported format
|
|
551
|
+
|
|
552
|
+
# Handle number/integer validation
|
|
553
|
+
elif obj.get("type") in ["number", "integer"]:
|
|
554
|
+
# These properties are supported
|
|
555
|
+
supported_props = [
|
|
556
|
+
"multipleOf",
|
|
557
|
+
"maximum",
|
|
558
|
+
"exclusiveMaximum",
|
|
559
|
+
"minimum",
|
|
560
|
+
"exclusiveMinimum",
|
|
561
|
+
]
|
|
562
|
+
# Remove any unsupported properties
|
|
563
|
+
for key in list(obj.keys()):
|
|
564
|
+
if key not in [
|
|
565
|
+
*supported_props,
|
|
566
|
+
"type",
|
|
567
|
+
"description",
|
|
568
|
+
"default",
|
|
569
|
+
]:
|
|
570
|
+
del obj[key]
|
|
571
|
+
|
|
572
|
+
# Process nested structures
|
|
573
|
+
for key in ["allOf", "oneOf", "$defs", "definitions"]:
|
|
574
|
+
if key in obj:
|
|
575
|
+
if isinstance(obj[key], list):
|
|
576
|
+
for i, item in enumerate(obj[key]):
|
|
577
|
+
_validate_and_fix_schema(
|
|
578
|
+
item, f"{path}.{key}[{i}]", False
|
|
579
|
+
)
|
|
580
|
+
elif isinstance(obj[key], dict):
|
|
581
|
+
for def_name, def_schema in obj[key].items():
|
|
582
|
+
_validate_and_fix_schema(
|
|
583
|
+
def_schema,
|
|
584
|
+
f"{path}.{key}.{def_name}",
|
|
585
|
+
False,
|
|
586
|
+
)
|
|
587
|
+
|
|
469
588
|
elif isinstance(obj, list):
|
|
470
|
-
for item in obj:
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
589
|
+
for i, item in enumerate(obj):
|
|
590
|
+
_validate_and_fix_schema(item, f"{path}[{i}]", False)
|
|
591
|
+
|
|
592
|
+
def _check_schema_limits(obj, counts=None):
|
|
593
|
+
r"""Check if schema exceeds OpenAI limits."""
|
|
594
|
+
if counts is None:
|
|
595
|
+
counts = {
|
|
596
|
+
"properties": 0,
|
|
597
|
+
"depth": 0,
|
|
598
|
+
"enums": 0,
|
|
599
|
+
"string_length": 0,
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
def _count_properties(o, depth=0):
|
|
603
|
+
if isinstance(o, dict):
|
|
604
|
+
if depth > 5:
|
|
605
|
+
raise ValueError(
|
|
606
|
+
"Schema exceeds maximum nesting depth of 5"
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
if o.get("type") == "object" and "properties" in o:
|
|
610
|
+
counts["properties"] += len(o["properties"])
|
|
611
|
+
for prop in o["properties"].values():
|
|
612
|
+
_count_properties(prop, depth + 1)
|
|
613
|
+
|
|
614
|
+
if "enum" in o:
|
|
615
|
+
counts["enums"] += len(o["enum"])
|
|
616
|
+
if isinstance(o["enum"], list):
|
|
617
|
+
for val in o["enum"]:
|
|
618
|
+
if isinstance(val, str):
|
|
619
|
+
counts["string_length"] += len(val)
|
|
620
|
+
|
|
621
|
+
# Count property names
|
|
622
|
+
if "properties" in o:
|
|
623
|
+
for name in o["properties"].keys():
|
|
624
|
+
counts["string_length"] += len(name)
|
|
625
|
+
|
|
626
|
+
# Process nested structures
|
|
627
|
+
for key in ["items", "allOf", "oneOf", "anyOf"]:
|
|
628
|
+
if key in o:
|
|
629
|
+
if isinstance(o[key], dict):
|
|
630
|
+
_count_properties(o[key], depth)
|
|
631
|
+
elif isinstance(o[key], list):
|
|
632
|
+
for item in o[key]:
|
|
633
|
+
_count_properties(item, depth)
|
|
634
|
+
|
|
635
|
+
_count_properties(obj)
|
|
636
|
+
|
|
637
|
+
# Check limits, reference: https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#objects-have-limitations-on-nesting-depth-and-size # noqa: E501
|
|
638
|
+
if counts["properties"] > 5000:
|
|
639
|
+
raise ValueError(
|
|
640
|
+
"Schema exceeds maximum of 5000 properties"
|
|
641
|
+
)
|
|
642
|
+
if counts["enums"] > 1000:
|
|
643
|
+
raise ValueError(
|
|
644
|
+
"Schema exceeds maximum of 1000 enum values"
|
|
645
|
+
)
|
|
646
|
+
if counts["string_length"] > 120000:
|
|
647
|
+
raise ValueError(
|
|
648
|
+
"Schema exceeds maximum total string length of 120000"
|
|
649
|
+
)
|
|
488
650
|
|
|
489
|
-
|
|
490
|
-
|
|
651
|
+
return True
|
|
652
|
+
|
|
653
|
+
# Check if schema has any issues that prevent strict mode
|
|
654
|
+
def _has_strict_mode_issues(obj):
|
|
655
|
+
r"""Check for any issues that would prevent strict mode."""
|
|
656
|
+
issues = []
|
|
657
|
+
|
|
658
|
+
def _check_issues(o, path=""):
|
|
659
|
+
if isinstance(o, dict):
|
|
660
|
+
# Check for additionalProperties: true
|
|
661
|
+
if o.get("additionalProperties") is True:
|
|
662
|
+
issues.append(
|
|
663
|
+
f"additionalProperties: true at {path}"
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
# Check for unsupported keywords
|
|
667
|
+
unsupported = [
|
|
668
|
+
"not",
|
|
669
|
+
"dependentRequired",
|
|
670
|
+
"dependentSchemas",
|
|
671
|
+
"if",
|
|
672
|
+
"then",
|
|
673
|
+
"else",
|
|
674
|
+
"patternProperties",
|
|
675
|
+
]
|
|
676
|
+
for keyword in unsupported:
|
|
677
|
+
if keyword in o:
|
|
678
|
+
issues.append(
|
|
679
|
+
f"Unsupported keyword '{keyword}' "
|
|
680
|
+
f"at {path}"
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
# Recursively check
|
|
684
|
+
for key, value in o.items():
|
|
685
|
+
if isinstance(value, (dict, list)):
|
|
686
|
+
_check_issues(value, f"{path}.{key}")
|
|
687
|
+
|
|
688
|
+
elif isinstance(o, list):
|
|
689
|
+
for i, item in enumerate(o):
|
|
690
|
+
_check_issues(item, f"{path}[{i}]")
|
|
691
|
+
|
|
692
|
+
_check_issues(obj)
|
|
693
|
+
return issues
|
|
694
|
+
|
|
695
|
+
# Check if already strict and compliant
|
|
491
696
|
if schema.get("function", {}).get("strict") is True:
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
697
|
+
# Validate it's actually compliant
|
|
698
|
+
try:
|
|
699
|
+
params = schema["function"].get("parameters", {})
|
|
700
|
+
if params:
|
|
701
|
+
_validate_and_fix_schema(params)
|
|
702
|
+
_check_schema_limits(params)
|
|
703
|
+
return tool
|
|
704
|
+
except Exception:
|
|
705
|
+
# Not actually compliant, continue to fix it
|
|
706
|
+
pass
|
|
707
|
+
|
|
708
|
+
# Apply sanitization first to handle optional fields properly
|
|
495
709
|
if "function" in schema:
|
|
710
|
+
# Apply the sanitization function first
|
|
711
|
+
from camel.toolkits.function_tool import (
|
|
712
|
+
sanitize_and_enforce_required,
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
schema = sanitize_and_enforce_required(schema)
|
|
716
|
+
|
|
717
|
+
# Special handling for schemas with additionalProperties that
|
|
718
|
+
# aren't false These can't use strict mode
|
|
719
|
+
def _has_open_props(obj, path=""):
|
|
720
|
+
"""Check if any object has additionalProperties that
|
|
721
|
+
isn't false."""
|
|
722
|
+
if isinstance(obj, dict):
|
|
723
|
+
if (
|
|
724
|
+
obj.get("type") == "object"
|
|
725
|
+
and "additionalProperties" in obj
|
|
726
|
+
):
|
|
727
|
+
if obj["additionalProperties"] is not False:
|
|
728
|
+
return True
|
|
729
|
+
|
|
730
|
+
# Recurse through the schema
|
|
731
|
+
for key, value in obj.items():
|
|
732
|
+
if key in [
|
|
733
|
+
"properties",
|
|
734
|
+
"items",
|
|
735
|
+
"allOf",
|
|
736
|
+
"oneOf",
|
|
737
|
+
"anyOf",
|
|
738
|
+
]:
|
|
739
|
+
if isinstance(value, dict):
|
|
740
|
+
if _has_open_props(value, f"{path}.{key}"):
|
|
741
|
+
return True
|
|
742
|
+
elif isinstance(value, list):
|
|
743
|
+
for i, item in enumerate(value):
|
|
744
|
+
if _has_open_props(
|
|
745
|
+
item,
|
|
746
|
+
f"{path}.{key}[{i}]",
|
|
747
|
+
):
|
|
748
|
+
return True
|
|
749
|
+
elif isinstance(value, dict) and key not in [
|
|
750
|
+
"description",
|
|
751
|
+
"type",
|
|
752
|
+
"enum",
|
|
753
|
+
]:
|
|
754
|
+
if _has_open_props(value, f"{path}.{key}"):
|
|
755
|
+
return True
|
|
756
|
+
return False
|
|
757
|
+
|
|
758
|
+
# Check if schema has dynamic additionalProperties
|
|
759
|
+
if _has_open_props(schema["function"].get("parameters", {})):
|
|
760
|
+
# Can't use strict mode with dynamic additionalProperties
|
|
761
|
+
schema["function"]["strict"] = False
|
|
762
|
+
tool.set_openai_tool_schema(schema)
|
|
763
|
+
logger.warning(
|
|
764
|
+
f"Tool '{tool.get_function_name()}' has "
|
|
765
|
+
f"dynamic additionalProperties and cannot use "
|
|
766
|
+
f"strict mode"
|
|
767
|
+
)
|
|
768
|
+
return tool
|
|
769
|
+
|
|
770
|
+
# Now check for blocking issues after sanitization
|
|
771
|
+
issues = _has_strict_mode_issues(schema)
|
|
772
|
+
if issues:
|
|
773
|
+
# Can't use strict mode
|
|
774
|
+
schema["function"]["strict"] = False
|
|
775
|
+
tool.set_openai_tool_schema(schema)
|
|
776
|
+
logger.warning(
|
|
777
|
+
f"Tool '{tool.get_function_name()}' has "
|
|
778
|
+
f"issues preventing strict mode: "
|
|
779
|
+
f"{'; '.join(issues[:3])}{'...' if len(issues) > 3 else ''}" # noqa: E501
|
|
780
|
+
)
|
|
781
|
+
return tool
|
|
782
|
+
|
|
783
|
+
# Enable strict mode
|
|
496
784
|
schema["function"]["strict"] = True
|
|
497
785
|
|
|
498
|
-
# Ensure parameters have proper strict mode configuration
|
|
499
786
|
parameters = schema["function"].get("parameters", {})
|
|
500
787
|
if parameters:
|
|
501
|
-
#
|
|
502
|
-
parameters
|
|
503
|
-
|
|
504
|
-
# Process properties to handle optional fields
|
|
505
|
-
properties = parameters.get("properties", {})
|
|
506
|
-
parameters["required"] = list(properties.keys())
|
|
507
|
-
|
|
508
|
-
# Apply the sanitization function from function_tool
|
|
509
|
-
from camel.toolkits.function_tool import (
|
|
510
|
-
sanitize_and_enforce_required,
|
|
511
|
-
)
|
|
788
|
+
# Validate and fix the parameters schema
|
|
789
|
+
_validate_and_fix_schema(parameters)
|
|
512
790
|
|
|
513
|
-
|
|
791
|
+
# Check schema limits
|
|
792
|
+
_check_schema_limits(parameters)
|
|
514
793
|
|
|
515
794
|
tool.set_openai_tool_schema(schema)
|
|
516
795
|
logger.debug(
|
|
@@ -518,7 +797,23 @@ class MCPToolkit(BaseToolkit):
|
|
|
518
797
|
)
|
|
519
798
|
|
|
520
799
|
except Exception as e:
|
|
521
|
-
|
|
800
|
+
# If we can't make it strict, disable strict mode
|
|
801
|
+
try:
|
|
802
|
+
if "function" in schema:
|
|
803
|
+
schema["function"]["strict"] = False
|
|
804
|
+
tool.set_openai_tool_schema(schema)
|
|
805
|
+
logger.warning(
|
|
806
|
+
f"Failed to ensure strict schema for "
|
|
807
|
+
f"tool '{tool.get_function_name()}': {str(e)[:100]}. "
|
|
808
|
+
f"Setting strict=False."
|
|
809
|
+
)
|
|
810
|
+
except Exception as inner_e:
|
|
811
|
+
# If even setting strict=False fails, log the error
|
|
812
|
+
logger.error(
|
|
813
|
+
f"Critical error processing "
|
|
814
|
+
f"tool '{tool.get_function_name()}': {inner_e}. "
|
|
815
|
+
f"Tool may not function correctly."
|
|
816
|
+
)
|
|
522
817
|
|
|
523
818
|
return tool
|
|
524
819
|
|
|
@@ -98,6 +98,7 @@ class TerminalToolkit(BaseToolkit):
|
|
|
98
98
|
self.is_macos = platform.system() == 'Darwin'
|
|
99
99
|
self.initial_env_path: Optional[str] = None
|
|
100
100
|
self.initial_env_prepared = False
|
|
101
|
+
self.uv_path: Optional[str] = None
|
|
101
102
|
|
|
102
103
|
atexit.register(self.__del__)
|
|
103
104
|
|
|
@@ -200,37 +201,111 @@ class TerminalToolkit(BaseToolkit):
|
|
|
200
201
|
f"Creating new Python environment at: {self.cloned_env_path}\n"
|
|
201
202
|
)
|
|
202
203
|
|
|
203
|
-
#
|
|
204
|
-
|
|
204
|
+
# Try to use uv if available
|
|
205
|
+
if self._ensure_uv_available():
|
|
206
|
+
# Use uv to create environment with current Python version
|
|
207
|
+
uv_command = self.uv_path if self.uv_path else "uv"
|
|
205
208
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
self.cloned_env_path, "Scripts", "python.exe"
|
|
209
|
+
# Get current Python version
|
|
210
|
+
current_version = (
|
|
211
|
+
f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
210
212
|
)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
|
|
214
|
+
subprocess.run(
|
|
215
|
+
[
|
|
216
|
+
uv_command,
|
|
217
|
+
"venv",
|
|
218
|
+
"--python",
|
|
219
|
+
current_version,
|
|
220
|
+
self.cloned_env_path,
|
|
221
|
+
],
|
|
222
|
+
check=True,
|
|
223
|
+
capture_output=True,
|
|
224
|
+
cwd=self.working_dir,
|
|
225
|
+
timeout=300,
|
|
214
226
|
)
|
|
215
227
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
228
|
+
# Get the python path from the new environment
|
|
229
|
+
if self.os_type == 'Windows':
|
|
230
|
+
python_path = os.path.join(
|
|
231
|
+
self.cloned_env_path, "Scripts", "python.exe"
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
python_path = os.path.join(
|
|
235
|
+
self.cloned_env_path, "bin", "python"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Install pip and setuptools using uv
|
|
219
239
|
subprocess.run(
|
|
220
|
-
[
|
|
240
|
+
[
|
|
241
|
+
uv_command,
|
|
242
|
+
"pip",
|
|
243
|
+
"install",
|
|
244
|
+
"--python",
|
|
245
|
+
python_path,
|
|
246
|
+
"pip",
|
|
247
|
+
"setuptools",
|
|
248
|
+
"wheel",
|
|
249
|
+
],
|
|
221
250
|
check=True,
|
|
222
251
|
capture_output=True,
|
|
223
252
|
cwd=self.working_dir,
|
|
224
|
-
timeout=
|
|
253
|
+
timeout=300,
|
|
225
254
|
)
|
|
255
|
+
|
|
226
256
|
self._update_terminal_output(
|
|
227
|
-
"
|
|
257
|
+
"[UV] Cloned Python environment created successfully!\n"
|
|
228
258
|
)
|
|
259
|
+
|
|
229
260
|
else:
|
|
261
|
+
# Fallback to standard venv
|
|
230
262
|
self._update_terminal_output(
|
|
231
|
-
|
|
263
|
+
"Falling back to standard venv for cloning environment\n"
|
|
232
264
|
)
|
|
233
265
|
|
|
266
|
+
# Create virtual environment with pip. On macOS, use
|
|
267
|
+
# symlinks=False to avoid dyld library loading issues
|
|
268
|
+
venv.create(
|
|
269
|
+
self.cloned_env_path, with_pip=True, symlinks=False
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Ensure pip is properly available by upgrading it
|
|
273
|
+
if self.os_type == 'Windows':
|
|
274
|
+
python_path = os.path.join(
|
|
275
|
+
self.cloned_env_path, "Scripts", "python.exe"
|
|
276
|
+
)
|
|
277
|
+
else:
|
|
278
|
+
python_path = os.path.join(
|
|
279
|
+
self.cloned_env_path, "bin", "python"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# Verify python executable exists
|
|
283
|
+
if os.path.exists(python_path):
|
|
284
|
+
# Use python -m pip to ensure pip is available
|
|
285
|
+
subprocess.run(
|
|
286
|
+
[
|
|
287
|
+
python_path,
|
|
288
|
+
"-m",
|
|
289
|
+
"pip",
|
|
290
|
+
"install",
|
|
291
|
+
"--upgrade",
|
|
292
|
+
"pip",
|
|
293
|
+
],
|
|
294
|
+
check=True,
|
|
295
|
+
capture_output=True,
|
|
296
|
+
cwd=self.working_dir,
|
|
297
|
+
timeout=60,
|
|
298
|
+
)
|
|
299
|
+
self._update_terminal_output(
|
|
300
|
+
"New Python environment created successfully "
|
|
301
|
+
"with pip!\n"
|
|
302
|
+
)
|
|
303
|
+
else:
|
|
304
|
+
self._update_terminal_output(
|
|
305
|
+
f"Warning: Python executable not found "
|
|
306
|
+
f"at {python_path}\n"
|
|
307
|
+
)
|
|
308
|
+
|
|
234
309
|
except subprocess.CalledProcessError as e:
|
|
235
310
|
error_msg = e.stderr.decode() if e.stderr else str(e)
|
|
236
311
|
self._update_terminal_output(
|
|
@@ -255,6 +330,97 @@ class TerminalToolkit(BaseToolkit):
|
|
|
255
330
|
or shutil.which("uv") is not None
|
|
256
331
|
)
|
|
257
332
|
|
|
333
|
+
def _ensure_uv_available(self) -> bool:
|
|
334
|
+
r"""Ensure uv is available, installing it if necessary.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
bool: True if uv is available (either already installed or
|
|
338
|
+
successfully installed), False otherwise.
|
|
339
|
+
"""
|
|
340
|
+
# Check if uv is already available
|
|
341
|
+
existing_uv = shutil.which("uv")
|
|
342
|
+
if existing_uv is not None:
|
|
343
|
+
self.uv_path = existing_uv
|
|
344
|
+
self._update_terminal_output(
|
|
345
|
+
f"uv is already available at: {self.uv_path}\n"
|
|
346
|
+
)
|
|
347
|
+
return True
|
|
348
|
+
|
|
349
|
+
try:
|
|
350
|
+
self._update_terminal_output("uv not found, installing...\n")
|
|
351
|
+
|
|
352
|
+
# Install uv using the official installer script
|
|
353
|
+
if self.os_type in ['Darwin', 'Linux']:
|
|
354
|
+
# Use curl to download and execute the installer
|
|
355
|
+
install_cmd = "curl -LsSf https://astral.sh/uv/install.sh | sh"
|
|
356
|
+
result = subprocess.run(
|
|
357
|
+
install_cmd,
|
|
358
|
+
shell=True,
|
|
359
|
+
capture_output=True,
|
|
360
|
+
text=True,
|
|
361
|
+
timeout=60,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if result.returncode != 0:
|
|
365
|
+
self._update_terminal_output(
|
|
366
|
+
f"Failed to install uv: {result.stderr}\n"
|
|
367
|
+
)
|
|
368
|
+
return False
|
|
369
|
+
|
|
370
|
+
# Check if uv was installed in the expected location
|
|
371
|
+
home = os.path.expanduser("~")
|
|
372
|
+
uv_bin_path = os.path.join(home, ".cargo", "bin")
|
|
373
|
+
uv_executable = os.path.join(uv_bin_path, "uv")
|
|
374
|
+
|
|
375
|
+
if os.path.exists(uv_executable):
|
|
376
|
+
# Store the full path to uv instead of modifying PATH
|
|
377
|
+
self.uv_path = uv_executable
|
|
378
|
+
self._update_terminal_output(
|
|
379
|
+
f"uv installed successfully at: {self.uv_path}\n"
|
|
380
|
+
)
|
|
381
|
+
return True
|
|
382
|
+
|
|
383
|
+
elif self.os_type == 'Windows':
|
|
384
|
+
# Use PowerShell to install uv on Windows
|
|
385
|
+
install_cmd = (
|
|
386
|
+
"powershell -ExecutionPolicy Bypass -c "
|
|
387
|
+
"\"irm https://astral.sh/uv/install.ps1 | iex\""
|
|
388
|
+
)
|
|
389
|
+
result = subprocess.run(
|
|
390
|
+
install_cmd,
|
|
391
|
+
shell=True,
|
|
392
|
+
capture_output=True,
|
|
393
|
+
text=True,
|
|
394
|
+
timeout=60,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
if result.returncode != 0:
|
|
398
|
+
self._update_terminal_output(
|
|
399
|
+
f"Failed to install uv: {result.stderr}\n"
|
|
400
|
+
)
|
|
401
|
+
return False
|
|
402
|
+
|
|
403
|
+
# Check if uv was installed in the expected location on Windows
|
|
404
|
+
home = os.path.expanduser("~")
|
|
405
|
+
uv_bin_path = os.path.join(home, ".cargo", "bin")
|
|
406
|
+
uv_executable = os.path.join(uv_bin_path, "uv.exe")
|
|
407
|
+
|
|
408
|
+
if os.path.exists(uv_executable):
|
|
409
|
+
# Store the full path to uv instead of modifying PATH
|
|
410
|
+
self.uv_path = uv_executable
|
|
411
|
+
self._update_terminal_output(
|
|
412
|
+
f"uv installed successfully at: {self.uv_path}\n"
|
|
413
|
+
)
|
|
414
|
+
return True
|
|
415
|
+
|
|
416
|
+
self._update_terminal_output("Failed to verify uv installation\n")
|
|
417
|
+
return False
|
|
418
|
+
|
|
419
|
+
except Exception as e:
|
|
420
|
+
self._update_terminal_output(f"Error installing uv: {e!s}\n")
|
|
421
|
+
logger.error(f"Failed to install uv: {e}")
|
|
422
|
+
return False
|
|
423
|
+
|
|
258
424
|
def _prepare_initial_environment(self):
|
|
259
425
|
r"""Prepare initial environment with Python 3.10, pip, and other
|
|
260
426
|
essential tools.
|
|
@@ -279,10 +445,14 @@ class TerminalToolkit(BaseToolkit):
|
|
|
279
445
|
# Create the initial environment directory
|
|
280
446
|
os.makedirs(self.initial_env_path, exist_ok=True)
|
|
281
447
|
|
|
282
|
-
#
|
|
283
|
-
if self.
|
|
448
|
+
# Try to ensure uv is available and use it preferentially
|
|
449
|
+
if self._ensure_uv_available():
|
|
284
450
|
self._setup_initial_env_with_uv()
|
|
285
451
|
else:
|
|
452
|
+
# Fallback to venv if uv installation failed
|
|
453
|
+
self._update_terminal_output(
|
|
454
|
+
"Falling back to standard venv for environment setup\n"
|
|
455
|
+
)
|
|
286
456
|
self._setup_initial_env_with_venv()
|
|
287
457
|
|
|
288
458
|
self.initial_env_prepared = True
|
|
@@ -302,9 +472,18 @@ class TerminalToolkit(BaseToolkit):
|
|
|
302
472
|
raise Exception("Initial environment path not set")
|
|
303
473
|
|
|
304
474
|
try:
|
|
475
|
+
# Use the stored uv path if available, otherwise fall back to "uv"
|
|
476
|
+
uv_command = self.uv_path if self.uv_path else "uv"
|
|
477
|
+
|
|
305
478
|
# Create virtual environment with Python 3.10 using uv
|
|
306
479
|
subprocess.run(
|
|
307
|
-
[
|
|
480
|
+
[
|
|
481
|
+
uv_command,
|
|
482
|
+
"venv",
|
|
483
|
+
"--python",
|
|
484
|
+
"3.10",
|
|
485
|
+
self.initial_env_path,
|
|
486
|
+
],
|
|
308
487
|
check=True,
|
|
309
488
|
capture_output=True,
|
|
310
489
|
cwd=self.working_dir,
|
|
@@ -332,7 +511,7 @@ class TerminalToolkit(BaseToolkit):
|
|
|
332
511
|
]
|
|
333
512
|
subprocess.run(
|
|
334
513
|
[
|
|
335
|
-
|
|
514
|
+
uv_command,
|
|
336
515
|
"pip",
|
|
337
516
|
"install",
|
|
338
517
|
"--python",
|
|
@@ -366,10 +545,12 @@ class TerminalToolkit(BaseToolkit):
|
|
|
366
545
|
|
|
367
546
|
try:
|
|
368
547
|
# Create virtual environment with system Python
|
|
548
|
+
# On macOS, use symlinks=False to avoid dyld library loading issues
|
|
369
549
|
venv.create(
|
|
370
550
|
self.initial_env_path,
|
|
371
551
|
with_pip=True,
|
|
372
552
|
system_site_packages=False,
|
|
553
|
+
symlinks=False,
|
|
373
554
|
)
|
|
374
555
|
|
|
375
556
|
# Get pip path
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: camel-ai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.73a8
|
|
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
|
|
@@ -82,7 +82,7 @@ Requires-Dist: networkx<4,>=3.4.2; extra == 'all'
|
|
|
82
82
|
Requires-Dist: newspaper3k<0.3,>=0.2.8; extra == 'all'
|
|
83
83
|
Requires-Dist: notion-client<3,>=2.2.1; extra == 'all'
|
|
84
84
|
Requires-Dist: numpy<=2.2,>=1.2; extra == 'all'
|
|
85
|
-
Requires-Dist: onnxruntime<=1.
|
|
85
|
+
Requires-Dist: onnxruntime<=1.19.2; extra == 'all'
|
|
86
86
|
Requires-Dist: openapi-spec-validator<0.8,>=0.7.1; extra == 'all'
|
|
87
87
|
Requires-Dist: openpyxl>=3.1.5; extra == 'all'
|
|
88
88
|
Requires-Dist: pandas<2,>=1.5.3; extra == 'all'
|
|
@@ -213,7 +213,7 @@ Requires-Dist: docx2txt<0.9,>=0.8; extra == 'document-tools'
|
|
|
213
213
|
Requires-Dist: docx>=0.2.4; extra == 'document-tools'
|
|
214
214
|
Requires-Dist: markitdown>=0.1.1; extra == 'document-tools'
|
|
215
215
|
Requires-Dist: numpy<=2.2,>=1.2; extra == 'document-tools'
|
|
216
|
-
Requires-Dist: onnxruntime<=1.
|
|
216
|
+
Requires-Dist: onnxruntime<=1.19.2; extra == 'document-tools'
|
|
217
217
|
Requires-Dist: openapi-spec-validator<0.8,>=0.7.1; extra == 'document-tools'
|
|
218
218
|
Requires-Dist: openpyxl>=3.1.5; extra == 'document-tools'
|
|
219
219
|
Requires-Dist: pandasai<3,>=2.3.0; extra == 'document-tools'
|
|
@@ -268,7 +268,7 @@ Requires-Dist: mcp-server-fetch==2025.1.17; extra == 'owl'
|
|
|
268
268
|
Requires-Dist: mcp-simple-arxiv==0.2.2; extra == 'owl'
|
|
269
269
|
Requires-Dist: newspaper3k<0.3,>=0.2.8; extra == 'owl'
|
|
270
270
|
Requires-Dist: numpy<=2.2,>=1.2; extra == 'owl'
|
|
271
|
-
Requires-Dist: onnxruntime<=1.
|
|
271
|
+
Requires-Dist: onnxruntime<=1.19.2; extra == 'owl'
|
|
272
272
|
Requires-Dist: openapi-spec-validator<0.8,>=0.7.1; extra == 'owl'
|
|
273
273
|
Requires-Dist: openpyxl>=3.1.5; extra == 'owl'
|
|
274
274
|
Requires-Dist: pandas<2,>=1.5.3; extra == 'owl'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
camel/__init__.py,sha256=
|
|
1
|
+
camel/__init__.py,sha256=_AfDG2a7ery9GuUo4kqCeCo3IdXz3d2V-0ziFUoXkgc,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
|
|
@@ -7,7 +7,7 @@ camel/agents/__init__.py,sha256=64weKqdvmpZcGWyVkO-OKASAmVUdrQjv60JApgPk_SA,1644
|
|
|
7
7
|
camel/agents/_types.py,sha256=MeFZzay2kJA6evALQ-MbBTKW-0lu_0wBuKsxzH_4gWI,1552
|
|
8
8
|
camel/agents/_utils.py,sha256=AR7Qqgbkmn4X2edYUQf1rdksGUyV5hm3iK1z-Dn0Mcg,6266
|
|
9
9
|
camel/agents/base.py,sha256=c4bJYL3G3Z41SaFdMPMn8ZjLdFiFaVOFO6EQIfuCVR8,1124
|
|
10
|
-
camel/agents/chat_agent.py,sha256=
|
|
10
|
+
camel/agents/chat_agent.py,sha256=fxZ8dBVhLwA-ZsBJbOvTxvQc4zHCKi4D3qwLz2URiW0,151815
|
|
11
11
|
camel/agents/critic_agent.py,sha256=L6cTbYjyZB0DCa51tQ6LZLA6my8kHLC4nktHySH78H4,10433
|
|
12
12
|
camel/agents/deductive_reasoner_agent.py,sha256=6BZGaq1hR6hKJuQtOfoYQnk_AkZpw_Mr7mUy2MspQgs,13540
|
|
13
13
|
camel/agents/embodied_agent.py,sha256=XBxBu5ZMmSJ4B2U3Z7SMwvLlgp6yNpaBe8HNQmY9CZA,7536
|
|
@@ -268,7 +268,7 @@ camel/schemas/base.py,sha256=x0H0oIwbQR6UGdEvR5v-srI25MJ8uTrEw8nnygvLwjw,1604
|
|
|
268
268
|
camel/schemas/openai_converter.py,sha256=SEnYsYcboZgVmjcC1YP5xke3c0MYPESPRmYQWsDGZ4Y,4362
|
|
269
269
|
camel/schemas/outlines_converter.py,sha256=OYKPR1fNyrYs9eh5RiXEAccMbnRc9WTwSVJYbh9HkKE,8738
|
|
270
270
|
camel/services/agent_openapi_server.py,sha256=NUCBLNZBvi4C-J1ESMyRHiRX1NDhPdPkXTMJTl0oUQo,14698
|
|
271
|
-
camel/societies/__init__.py,sha256=
|
|
271
|
+
camel/societies/__init__.py,sha256=8J_PBAGkFdL0692Mxx8dvcSRm57ibouXdIU_f1n9dJE,876
|
|
272
272
|
camel/societies/babyagi_playing.py,sha256=KbTdpHfZ2V8AripVck0bNTOyF-RSaMPCRARz3DvzWfQ,11855
|
|
273
273
|
camel/societies/role_playing.py,sha256=0XScr3WfxX1QOC71RhBLmrcS5y2c7DMQB_mAFOHU34M,31421
|
|
274
274
|
camel/societies/workforce/__init__.py,sha256=bkTI-PE-MSK9AQ2V2gR6cR2WY-R7Jqy_NmXRtAoqo8o,920
|
|
@@ -347,7 +347,7 @@ camel/toolkits/klavis_toolkit.py,sha256=ZKerhgz5e-AV-iv0ftf07HgWikknIHjB3EOQswfu
|
|
|
347
347
|
camel/toolkits/linkedin_toolkit.py,sha256=wn4eXwYYlVA7doTna7k7WYhUqTBF83W79S-UJs_IQr0,8065
|
|
348
348
|
camel/toolkits/markitdown_toolkit.py,sha256=Cwga4sOTT1HO51CjmXubD6ieRMhumtqEN6jlsgL1nq0,2867
|
|
349
349
|
camel/toolkits/math_toolkit.py,sha256=KdI8AJ9Dbq5cfWboAYJUYgSkmADMCO5eZ6yqYkWuiIQ,3686
|
|
350
|
-
camel/toolkits/mcp_toolkit.py,sha256=
|
|
350
|
+
camel/toolkits/mcp_toolkit.py,sha256=orWwKwCCL7jiPPkuoydGcM97iDRZG3udabY1VyoyPNo,40542
|
|
351
351
|
camel/toolkits/memory_toolkit.py,sha256=TeKYd5UMwgjVpuS2orb-ocFL13eUNKujvrFOruDCpm8,4436
|
|
352
352
|
camel/toolkits/meshy_toolkit.py,sha256=NbgdOBD3FYLtZf-AfonIv6-Q8-8DW129jsaP1PqI2rs,7126
|
|
353
353
|
camel/toolkits/message_agent_toolkit.py,sha256=yWvAaxoxAvDEtD7NH7IkkHIyfWIYK47WZhn5E_RaxKo,22661
|
|
@@ -378,7 +378,7 @@ camel/toolkits/slack_toolkit.py,sha256=ZnK_fRdrQiaAUpjkgHSv1iXdv1HKEgXMlKevu3Qiv
|
|
|
378
378
|
camel/toolkits/stripe_toolkit.py,sha256=07swo5znGTnorafC1uYLKB4NRcJIOPOx19J7tkpLYWk,10102
|
|
379
379
|
camel/toolkits/sympy_toolkit.py,sha256=BAQnI8EFJydNUpKQWXBdleQ1Cm-srDBhFlqp9V9pbPQ,33757
|
|
380
380
|
camel/toolkits/task_planning_toolkit.py,sha256=Ttw9fHae4omGC1SA-6uaeXVHJ1YkwiVloz_hO-fm1gw,4855
|
|
381
|
-
camel/toolkits/terminal_toolkit.py,sha256=
|
|
381
|
+
camel/toolkits/terminal_toolkit.py,sha256=7LdcG0hqZaPiaJPagXVXi87Ncm-MJIIbFUTZRtrIXes,68194
|
|
382
382
|
camel/toolkits/thinking_toolkit.py,sha256=nZYLvKWIx2BM1DYu69I9B5EISAG7aYcLYXKv9663BVk,8000
|
|
383
383
|
camel/toolkits/twitter_toolkit.py,sha256=Px4N8aUxUzy01LhGSWkdrC2JgwKkrY3cvxgMeJ2XYfU,15939
|
|
384
384
|
camel/toolkits/video_analysis_toolkit.py,sha256=h6D7b1MAAzaHn222n_YKtwG-EGEGgMt7mBrNNVipYZc,23361
|
|
@@ -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.
|
|
471
|
-
camel_ai-0.2.
|
|
472
|
-
camel_ai-0.2.
|
|
473
|
-
camel_ai-0.2.
|
|
470
|
+
camel_ai-0.2.73a8.dist-info/METADATA,sha256=JMMFteW3ZHW4JreEL0nbvm9ll-rUoSab_ILr3csAPkE,50434
|
|
471
|
+
camel_ai-0.2.73a8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
472
|
+
camel_ai-0.2.73a8.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
|
|
473
|
+
camel_ai-0.2.73a8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|