File size: 4,639 Bytes
fcaa164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
import json
import re
from typing import Any, Dict, List, Optional

from camel.messages.conversion import (
    ToolCall,
    ToolResponse,
)
from camel.messages.conversion.sharegpt.function_call_formatter import (
    FunctionCallFormatter,
)


class HermesToolResponse(ToolResponse):
    r"""Represents a single tool/function call with validation"""

    pass


class HermesToolCall(ToolCall):
    r"""Represents a single tool/function call with validation"""

    pass


class HermesFunctionFormatter(
    FunctionCallFormatter[HermesToolCall, HermesToolResponse]
):
    r"""Hermes-style function calling format implementation with validation"""

    def extract_tool_calls(self, message: str) -> List[HermesToolCall]:
        r"""Extracts all tool calls from the provided message string.

        Args:
            message (str): The input message string containing potential tool
                calls.

        Returns:
            List[HermesToolCall]: A list of parsed HermesToolCall objects.
        """
        tool_calls = []
        pattern = r"<tool_call>\s*({.*?})\s*</tool_call>"
        matches = re.finditer(pattern, message, re.DOTALL)

        for match in matches:
            try:
                call_dict = json.loads(match.group(1).replace("'", '"'))
                tool_calls.append(HermesToolCall.model_validate(call_dict))
            except Exception as e:
                print(f"Warning: Failed to parse tool call: {e}")
                continue

        return tool_calls

    def extract_tool_response(
        self, message: str
    ) -> Optional[HermesToolResponse]:
        r"""Extracts a single tool response from the provided message string.

        Args:
            message (str): The input message string containing a potential
                tool response.

        Returns:
            Optional[HermesToolResponse]: A parsed HermesToolResponse object,
                or None if no valid response is found.
        """
        pattern = r"<tool_response>\s*({.*?})\s*</tool_response>"
        match = re.search(pattern, message, re.DOTALL)

        if match:
            try:
                response_json = match.group(1)
                response_dict = json.loads(response_json.replace("'", '"'))
                return HermesToolResponse.model_validate(response_dict)
            except Exception as e:
                print(f"Warning: Failed to parse tool response: {e}")
                return None
        return None

    def format_tool_call(
        self, content: str, func_name: str, args: Dict[str, Any]
    ) -> str:
        r"""Formats a tool call message with the given content, function name,
        and arguments.

        Args:
            content (str): The content or message to be included in the tool
                call.
            func_name (str): The name of the function being called.
            args (Dict[str, Any]): A dictionary of arguments to be passed to
                the function.

        Returns:
            str: A formatted string representing the tool call in Hermes
                format.
        """
        tool_call_dict = {"name": func_name, "arguments": args}

        if content:
            return f"{content}\n<tool_call>\n{tool_call_dict}\n</tool_call>"
        return f"<tool_call>\n{tool_call_dict}\n</tool_call>"

    def format_tool_response(self, func_name: str, result: Any) -> str:
        r"""Formats a tool response message with the given function name and
        result.

        Args:
            func_name (str): The name of the function whose result is being
                returned.
            result (Any): The result to be included in the tool response.

        Returns:
            str: A formatted string representing the tool response in Hermes
                format.
        """
        response_dict = {"name": func_name, "content": result}
        return f"<tool_response>\n{response_dict}\n</tool_response>"