# Copyright (c) ModelScope Contributors. All rights reserved. import json import re from typing import List, Optional, Tuple, Union from swift.infer_engine import Function from swift.template import Prompt from .base import BaseAgentTemplate class HermesAgentTemplate(BaseAgentTemplate): def get_toolcall(self, response: str) -> List[Function]: res_list = re.findall(r'(.+?)', response, re.DOTALL) functions = [] for res in res_list: res = self._parse_json(res) if isinstance(res, dict) and 'name' in res and 'arguments' in res: functions.append(Function(name=res['name'], arguments=res['arguments'])) if len(functions) == 0: # compat react_en return super().get_toolcall(response) return functions def _get_tool_responses(self, tool_messages): res_tool = [] for tool_message in tool_messages: tool_content = tool_message['content'] res_tool.append(f'\n{tool_content}\n') return '\n'.join(res_tool) def _get_tool_calls(self, tool_calls: List[str]): return '\n'.join(tool_calls) def _format_tool_responses( self, assistant_content: str, tool_messages, ) -> Tuple[str, 'Prompt']: with_action = self.keyword.action in assistant_content and self.keyword.action_input in assistant_content if with_action: return super()._format_tool_responses(assistant_content, tool_messages) if hasattr(self, 'template_meta'): prompt = self.template_meta.prompt chat_sep = self.template_meta.chat_sep else: prompt = ['<|im_start|>user\n{{QUERY}}<|im_end|>\n<|im_start|>assistant\n'] chat_sep = ['<|im_end|>\n'] res = chat_sep.copy() total_tool = self._get_tool_responses(tool_messages) for context in prompt: if isinstance(context, str): context = context.replace('{{QUERY}}', total_tool) res.append(context) return assistant_content, res def _format_tools(self, tools: List[Union[str, dict]], system: Optional[str] = None, user_message=None) -> str: tool_descs = [json.dumps(self.wrap_tool(tool), ensure_ascii=False) for tool in tools] system = system or '' return f"""{system} # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within XML tags: """ + '\n'.join(tool_descs) + """ For each function call, return a json object with function name and arguments within XML tags: {"name": , "arguments": } """ def _format_tool_calls(self, tool_call_messages): tool_calls = [] for message in tool_call_messages: tool_call = self._parse_tool_call(message['content']) tool_calls.append(f'\n{json.dumps(tool_call, ensure_ascii=False)}\n') return self._get_tool_calls(tool_calls) class HunyuanHermesAgentTemplate(HermesAgentTemplate): def get_toolcall(self, response: str) -> List[Function]: res_list = re.findall(r'(.+?)\n```json(.+?)```', response, re.DOTALL) functions = [] for name, arguments in res_list: arguments = self._parse_json(arguments) functions.append(Function(name=name, arguments=arguments)) if len(functions) == 0: # compat react_en return super().get_toolcall(response) return functions def _get_tool_responses(self, tool_messages): res_tool = [] for tool_message in tool_messages: tool_content = tool_message['content'] res_tool.append(f'{tool_content}') tool_responses = '\n'.join(res_tool) return f'{tool_responses}' def _get_tool_calls(self, tool_calls: List[str]): tool_calls = '\n'.join(tool_calls) return f'\n{tool_calls}\n' def _format_tools(self, tools: List[Union[str, dict]], system: Optional[str] = None, user_message=None) -> str: tool_descs = [json.dumps(self.wrap_tool(tool), ensure_ascii=False) for tool in tools] system = system or '' if system: system = f'{system}\n\n' return f"""{system}# Tools You may call one or more functions to assist with the user query. You are provided with function signatures within XML tags: """ + '\n'.join(tool_descs) + """ For function call returns, you should first print For each function call, you should return object like: function_name ```json function_arguments_in_json_format ```At the end of function call returns, you should print """