Skip to content

Commit ad14a38

Browse files
authored
chore: Small enhancement and fix (camel-ai#1599)
1 parent e77aaca commit ad14a38

File tree

17 files changed

+320
-29
lines changed

17 files changed

+320
-29
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM python:3.9-slim
2+
3+
# Install R and required dependencies
4+
RUN apt-get update && apt-get install -y \
5+
r-base \
6+
&& rm -rf /var/lib/apt/lists/*
7+
8+
# Set working directory
9+
WORKDIR /workspace
10+
11+
# Keep container running
12+
CMD ["tail", "-f", "/dev/null"]

camel/interpreters/docker_interpreter.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ class DockerInterpreter(BaseInterpreter):
5252
_CODE_EXECUTE_CMD_MAPPING: ClassVar[Dict[str, str]] = {
5353
"python": "python {file_name}",
5454
"bash": "bash {file_name}",
55+
"r": "Rscript {file_name}",
5556
}
5657

5758
_CODE_EXTENSION_MAPPING: ClassVar[Dict[str, str]] = {
5859
"python": "py",
5960
"bash": "sh",
61+
"r": "R",
6062
}
6163

6264
_CODE_TYPE_MAPPING: ClassVar[Dict[str, str]] = {
@@ -67,6 +69,8 @@ class DockerInterpreter(BaseInterpreter):
6769
"shell": "bash",
6870
"bash": "bash",
6971
"sh": "bash",
72+
"r": "r",
73+
"R": "r",
7074
}
7175

7276
def __init__(
@@ -104,8 +108,22 @@ def _initialize_if_needed(self) -> None:
104108
import docker
105109

106110
client = docker.from_env()
111+
112+
# Build custom image with Python and R
113+
dockerfile_path = Path(__file__).parent / "docker"
114+
image_tag = "camel-interpreter:latest"
115+
try:
116+
client.images.get(image_tag)
117+
except docker.errors.ImageNotFound:
118+
logger.info("Building custom interpreter image...")
119+
client.images.build(
120+
path=str(dockerfile_path),
121+
tag=image_tag,
122+
rm=True,
123+
)
124+
107125
self._container = client.containers.run(
108-
"python:3.10",
126+
image_tag,
109127
detach=True,
110128
name=f"camel-interpreter-{uuid.uuid4()}",
111129
command="tail -f /dev/null",

camel/interpreters/subprocess_interpreter.py

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ class SubprocessInterpreter(BaseInterpreter):
4848
_CODE_EXECUTE_CMD_MAPPING: ClassVar[Dict[str, str]] = {
4949
"python": "python {file_name}",
5050
"bash": "bash {file_name}",
51+
"r": "Rscript {file_name}",
5152
}
5253

5354
_CODE_EXTENSION_MAPPING: ClassVar[Dict[str, str]] = {
5455
"python": "py",
5556
"bash": "sh",
57+
"r": "R",
5658
}
5759

5860
_CODE_TYPE_MAPPING: ClassVar[Dict[str, str]] = {
@@ -63,6 +65,8 @@ class SubprocessInterpreter(BaseInterpreter):
6365
"shell": "bash",
6466
"bash": "bash",
6567
"sh": "bash",
68+
"r": "r",
69+
"R": "r",
6670
}
6771

6872
def __init__(
@@ -98,7 +102,7 @@ def run_file(
98102
if not file.is_file():
99103
raise RuntimeError(f"{file} is not a file.")
100104
code_type = self._check_code_type(code_type)
101-
if code_type == "python":
105+
if self._CODE_TYPE_MAPPING[code_type] == "python":
102106
# For Python code, use ast to analyze and modify the code
103107
import ast
104108

@@ -113,23 +117,41 @@ def run_file(
113117
# Get the last node
114118
if tree.body:
115119
last_node = tree.body[-1]
116-
# If it's an expression, wrap it in a print
120+
# Handle expressions that would normally not produce output
121+
# For example: In a REPL, typing '1 + 2' should show '3'
122+
117123
if isinstance(last_node, ast.Expr):
118-
tree.body[-1] = ast.Expr(
119-
value=ast.Call(
120-
func=ast.Name(id='print', ctx=ast.Load()),
121-
args=[
122-
ast.Call(
123-
func=ast.Name(
124-
id='repr', ctx=ast.Load()
125-
),
126-
args=[last_node.value],
127-
keywords=[],
128-
)
129-
],
130-
keywords=[],
124+
# Only wrap in print(repr()) if it's not already a
125+
# print call
126+
if not (
127+
isinstance(last_node.value, ast.Call)
128+
and isinstance(last_node.value.func, ast.Name)
129+
and last_node.value.func.id == 'print'
130+
):
131+
# Transform the AST to wrap the expression in print
132+
# (repr())
133+
# Example transformation:
134+
# Before: x + y
135+
# After: print(repr(x + y))
136+
tree.body[-1] = ast.Expr(
137+
value=ast.Call(
138+
# Create print() function call
139+
func=ast.Name(id='print', ctx=ast.Load()),
140+
args=[
141+
ast.Call(
142+
# Create repr() function call
143+
func=ast.Name(
144+
id='repr', ctx=ast.Load()
145+
),
146+
# Pass the original expression as
147+
# argument to repr()
148+
args=[last_node.value],
149+
keywords=[],
150+
)
151+
],
152+
keywords=[],
153+
)
131154
)
132-
)
133155
# Fix missing source locations
134156
ast.fix_missing_locations(tree)
135157
# Convert back to source
@@ -159,7 +181,10 @@ def run_file(
159181
return_code = proc.returncode
160182

161183
# Clean up temporary file if it was created
162-
if code_type == "python" and 'temp_file' in locals():
184+
if (
185+
self._CODE_TYPE_MAPPING[code_type] == "python"
186+
and 'temp_file' in locals()
187+
):
163188
temp_file.unlink()
164189

165190
if self.print_stdout and stdout:

camel/toolkits/sympy_toolkit.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,43 @@ def compute_rank(self, matrix: List[List[float]]) -> str:
721721
except Exception as e:
722722
return self.handle_exception("compute_rank", e)
723723

724+
def compute_inner_product(
725+
self, vector1: List[float], vector2: List[float]
726+
) -> str:
727+
r"""Computes the inner (dot) product of two vectors.
728+
729+
Args:
730+
vector1 (List[float]): The first vector as a list of floats.
731+
vector2 (List[float]): The second vector as a list of floats.
732+
733+
Returns:
734+
str: JSON string containing the inner product in the `"result"`
735+
field. If an error occurs, the JSON string will include an
736+
`"error"` field with the corresponding error message.
737+
738+
Raises:
739+
ValueError: If the vectors have different dimensions.
740+
"""
741+
import sympy as sp
742+
743+
try:
744+
# Convert the lists into sympy Matrix objects (column vectors)
745+
v1 = sp.Matrix(vector1)
746+
v2 = sp.Matrix(vector2)
747+
748+
# Check that the vectors have the same dimensions.
749+
if v1.shape != v2.shape:
750+
raise ValueError(
751+
"Vectors must have the same dimensions to compute "
752+
"the inner product."
753+
)
754+
755+
# Compute the dot (inner) product.
756+
inner_product = v1.dot(v2)
757+
return json.dumps({"result": str(inner_product)})
758+
except Exception as e:
759+
return self.handle_exception("compute_inner_product", e)
760+
724761
def handle_exception(self, func_name: str, error: Exception) -> str:
725762
r"""Handles exceptions by logging and returning error details.
726763
@@ -775,4 +812,5 @@ def get_tools(self) -> List[FunctionTool]:
775812
FunctionTool(self.compute_eigenvectors),
776813
FunctionTool(self.compute_nullspace),
777814
FunctionTool(self.compute_rank),
815+
FunctionTool(self.compute_inner_product),
778816
]

docs/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Main Documentation
3434
:caption: Key Modules
3535
:name: key_modules
3636

37+
key_modules/agents.md
38+
key_modules/datagen.md
3739
key_modules/models.md
3840
key_modules/messages.md
3941
key_modules/memory.md

docs/key_modules/agents.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Agents
2+
3+
## 1. Concept
4+
5+
Agents in CAMEL are autonomous entities capable of performing specific tasks through interaction with language models and other components. Each agent is designed with a particular role and capability, allowing them to work independently or collaboratively to achieve complex goals.
6+
7+
### 1.1. Base Agent Architecture
8+
All CAMEL agents inherit from the `BaseAgent` abstract class, which defines two core methods:
9+
- `reset()`: Resets the agent to its initial state
10+
- `step()`: Performs a single step of the agent's operation
11+
12+
### 1.2. Chat Agent
13+
The `ChatAgent` is the primary implementation that handles conversations with language models. It supports:
14+
- System message configuration for role definition
15+
- Memory management for conversation history
16+
- Tool/function calling capabilities
17+
- Response formatting and structured outputs
18+
- Multiple model backend support with scheduling strategies
19+
- Async operation support
20+
21+
## 2. Types
22+
23+
### 2.1. `ChatAgent`
24+
The main agent implementation for handling conversations with language models. Features include:
25+
- Tool integration and management
26+
- Memory management with customizable window sizes
27+
- Output language control
28+
- Response termination handling
29+
- Structured output support via Pydantic models
30+
31+
### 2.2. `CriticAgent`
32+
Specialized agent for evaluating and critiquing responses or solutions. Used in scenarios requiring quality assessment or validation.
33+
34+
### 2.3. `DeductiveReasonerAgent`
35+
Agent focused on logical reasoning and deduction. Breaks down complex problems into smaller, manageable steps.
36+
37+
### 2.4. `EmbodiedAgent`
38+
Agent designed for embodied AI scenarios, capable of understanding and responding to physical world contexts.
39+
40+
### 2.5. `KnowledgeGraphAgent`
41+
Specialized in building and utilizing knowledge graphs for enhanced reasoning and information management.
42+
43+
### 2.6. `MultiHopGeneratorAgent`
44+
Agent designed for handling multi-hop reasoning tasks, generating intermediate steps to reach conclusions.
45+
46+
### 2.7. `SearchAgent`
47+
Focused on information retrieval and search tasks across various data sources.
48+
49+
### 2.8. `TaskAgent`
50+
Handles task decomposition and management, breaking down complex tasks into manageable subtasks.
51+
52+
## 3. Usage
53+
54+
### 3.1. Basic Chat Agent Usage
55+
```python
56+
from camel.agents import ChatAgent
57+
58+
# Create a chat agent with a system message
59+
agent = ChatAgent(system_message="You are a helpful assistant.")
60+
61+
# Step through a conversation
62+
response = agent.step("Hello, can you help me?")
63+
```
64+
65+
### 3.2. Using Tools with Chat Agent
66+
```python
67+
from camel.agents import ChatAgent
68+
from camel.functions import FunctionTool
69+
70+
# Define a tool
71+
def calculator(a: int, b: int) -> int:
72+
return a + b
73+
74+
# Create agent with tool
75+
agent = ChatAgent(tools=[calculator])
76+
77+
# The agent can now use the calculator tool in conversations
78+
response = agent.step("What is 5 + 3?")
79+
```
80+
81+
### 3.3. Structured Output
82+
```python
83+
from pydantic import BaseModel
84+
from typing import List
85+
86+
class ResponseFormat(BaseModel):
87+
points: List[str]
88+
summary: str
89+
90+
# Create agent with structured output
91+
agent = ChatAgent()
92+
response = agent.step("List benefits of exercise", response_format=ResponseFormat)
93+
```
94+
95+
## 4. Best Practices
96+
97+
### 4.1. Memory Management
98+
- Use appropriate window sizes to manage conversation history
99+
- Consider token limits when dealing with long conversations
100+
- Utilize the memory system for maintaining context
101+
102+
### 4.2. Tool Integration
103+
- Keep tool functions focused and well-documented
104+
- Handle tool errors gracefully
105+
- Use external tools for operations that should be handled by the user
106+
107+
### 4.3. Response Handling
108+
- Implement appropriate response terminators for conversation control
109+
- Use structured outputs when specific response formats are needed
110+
- Handle async operations properly when dealing with long-running tasks
111+
112+
## 5. Advanced Features
113+
114+
### 5.1. Model Scheduling
115+
The agent supports multiple model backends with customizable scheduling strategies:
116+
```python
117+
def custom_strategy(models):
118+
# Custom model selection logic
119+
return models[0]
120+
121+
agent.add_model_scheduling_strategy("custom", custom_strategy)
122+
```
123+
124+
### 5.2. Output Language Control
125+
Control the language of agent responses:
126+
```python
127+
agent.set_output_language("Spanish")
128+
```

docs/key_modules/datagen.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
# Data Generation Modules
1+
# Data Generation
22

3-
This document describes the key data generation modules in CAMEL: Chain of Thought (CoT), Self-Instruct, Source2Synth, and STaR.
3+
This document describes CAMEL's key data generation modules that enable high-quality training data creation through advanced reasoning and instruction tuning techniques. The modules include:
4+
5+
- Chain of Thought (CoT): Generates explicit reasoning paths
6+
- Self-Instruct: Creates diverse instruction-following data
7+
- Source2Synth: Converts source code into natural language
8+
- Self-Improving CoT: Iteratively refines reasoning chains through self-critique
49

510
## Chain of Thought (CoT) Data Generation
611

examples/ai_society/babyagi_playing.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ def main(model=None, chat_turn_limit=15) -> None:
2626
user_role_name="Stock Trader",
2727
task_prompt=task_prompt,
2828
task_specify_agent_kwargs=dict(model=model),
29-
message_window_size=5,
3029
)
3130

3231
print(

examples/bots/discord_bot.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ def __init__(
9696

9797
self._agent = ChatAgent(
9898
assistant_sys_msg,
99-
message_window_size=10,
10099
)
101100

102101
self._auto_retriever = None

examples/bots/discord_bot_use_msg_queue.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ def __init__(
9292

9393
self._agent = ChatAgent(
9494
assistant_sys_msg,
95-
message_window_size=10,
9695
)
9796

9897
self._auto_retriever = None

0 commit comments

Comments
 (0)