Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions camel/agents/chat_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,6 @@ def update_memory(
timestamp (Optional[float], optional): Custom timestamp for the
memory record. If `None`, the current time will be used.
(default: :obj:`None`)
(default: obj:`None`)
"""
record = MemoryRecord(
message=message,
Expand Down Expand Up @@ -3552,7 +3551,9 @@ def _record_tool_calling(
base_timestamp = current_time_ns / 1_000_000_000 # Convert to seconds

self.update_memory(
assist_msg, OpenAIBackendRole.ASSISTANT, timestamp=base_timestamp
assist_msg,
OpenAIBackendRole.ASSISTANT,
timestamp=base_timestamp,
)

# Add minimal increment to ensure function message comes after
Expand All @@ -3562,6 +3563,41 @@ def _record_tool_calling(
timestamp=base_timestamp + 1e-6,
)

# Process tool output through the architecture if tool has output
# manager
if (
hasattr(self, '_internal_tools')
and func_name in self._internal_tools
):
tool = self._internal_tools[func_name]
if hasattr(tool, 'func') and hasattr(tool.func, '__self__'):
toolkit_instance = tool.func.__self__
if hasattr(toolkit_instance, 'process_tool_output'):
try:
toolkit_instance.process_tool_output(
tool_name=func_name,
tool_call_id=tool_call_id,
raw_result=result,
agent_id=self.agent_id,
timestamp=base_timestamp + 1e-6,
)
except Exception as e:
# Determine log level based on exception type
if isinstance(
e, (AttributeError, ValueError, TypeError)
):
logger.warning(
f"Error in tool output processing for "
f"{func_name}: {e.__class__.__name__}: {e}"
)
else:
logger.error(
f"Unexpected error in tool output "
f"processing for {func_name}: "
f"{e.__class__.__name__}: {e}",
exc_info=True,
)

# Record information about this tool call
tool_record = ToolCallingRecord(
tool_name=func_name,
Expand Down
56 changes: 55 additions & 1 deletion camel/toolkits/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========

from typing import TYPE_CHECKING, List, Literal, Optional
from typing import TYPE_CHECKING, Any, List, Literal, Optional

from camel.logger import get_logger
from camel.toolkits import FunctionTool
from camel.toolkits.output_processors import (
ToolOutputContext,
ToolOutputManager,
ToolOutputProcessor,
)
from camel.utils import AgentOpsMeta, with_timeout

if TYPE_CHECKING:
Expand Down Expand Up @@ -43,6 +48,55 @@ def __init__(self, timeout: Optional[float] = None):
raise ValueError("Timeout must be a positive number.")
self.timeout = timeout

# Initialize output management
self.output_manager = ToolOutputManager()

def register_output_processor(
self, processor: ToolOutputProcessor
) -> None:
r"""Register an output processor for tool results.

Args:
processor: The output processor to register.
"""
self.output_manager.register_processor(processor)

def process_tool_output(
self,
tool_name: str,
tool_call_id: str,
raw_result: Any,
agent_id: str,
timestamp: Optional[float] = None,
) -> ToolOutputContext:
r"""Process tool output through registered processors.

Args:
tool_name (str): Name of the tool that produced the output.
tool_call_id (str): Unique identifier for the tool call.
raw_result (Any): Raw output from the tool.
agent_id (str): ID of the agent that made the tool call.
timestamp (Optional[float]): Timestamp of the tool call.

Returns:
Processed tool output context.

Raises:
ValueError: If required parameters are missing or invalid.
"""
if not tool_name or not tool_call_id or not agent_id:
raise ValueError(
"tool_name, tool_call_id, and agent_id are required"
)

return self.output_manager.process_tool_output(
tool_name=tool_name,
tool_call_id=tool_call_id,
raw_result=raw_result,
agent_id=agent_id,
timestamp=timestamp,
)

# Add timeout to all callable methods in the toolkit
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Expand Down
Loading