From bed25f298f1f5e4c25cbeb3b411b630aad2b1410 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Tue, 27 Feb 2024 17:31:26 +0800 Subject: [PATCH 01/13] Feat: AgentLegoToolkit --- examples/internlm2_agent_web_demo.py | 6 +- lagent/actions/agentlego_wrapper.py | 104 +++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 lagent/actions/agentlego_wrapper.py diff --git a/examples/internlm2_agent_web_demo.py b/examples/internlm2_agent_web_demo.py index 36b4235f..6d729e33 100644 --- a/examples/internlm2_agent_web_demo.py +++ b/examples/internlm2_agent_web_demo.py @@ -11,6 +11,8 @@ from lagent.llms.meta_template import INTERNLM2_META as META from lagent.schema import AgentStatusCode +from lagent.actions.agentlego_wrapper import AgentLegoToolkit + # from streamlit.logger import get_logger @@ -22,7 +24,9 @@ def init_state(self): st.session_state['user'] = [] action_list = [ - ArxivSearch(), + # ArxivSearch(), + AgentLegoToolkit(type='ImageDescription', url='http://127.0.0.1:16180/openapi.json'), + AgentLegoToolkit(type='Calculator', url='http://127.0.0.1:16181/openapi.json') ] st.session_state['plugin_map'] = { action.name: action diff --git a/lagent/actions/agentlego_wrapper.py b/lagent/actions/agentlego_wrapper.py new file mode 100644 index 00000000..bf55cd3b --- /dev/null +++ b/lagent/actions/agentlego_wrapper.py @@ -0,0 +1,104 @@ +from typing import List, Union, Optional +from copy import deepcopy +from lagent import BaseAction, ActionReturn, ActionStatusCode +from lagent.actions.parser import ParseError, JsonParser + +from agentlego.tools.remote import RemoteTool +from agentlego.parsers import DefaultParser + + +class AgentLegoToolkit(BaseAction): + """A wrapper to align with the interface of iLagent tools.""" + + def __init__(self, + type: str, + url: Optional[str] = None, + text: Optional[str] = None, + spec_dict: Optional[dict] = None, + parser=JsonParser, + enable: bool = True): + + if url is not None: + spec = dict(url=url) + elif text is not None: + spec = dict(text=text) + else: + assert spec_dict is not None + spec = dict(spec_dict=spec_dict) + api_list = [api for api in RemoteTool.from_openapi(**spec)] + api_desc = [] + for api in api_list: + api.set_parser(DefaultParser) + desc = deepcopy(api.toolmeta.__dict__) + if not desc.get('parameters') and desc.get('inputs'): + parameters = [vars(param) for param in desc['inputs']] + for param in parameters: + if 'type' in param and isinstance(param['type'], object): + param['type'] = param['type'].__name__ + desc['parameters'] = parameters + desc.pop('inputs') + if not desc.get('return_data') and desc.get('outputs'): + return_data = [vars(param) for param in desc['outputs']] + for param in return_data: + if 'type' in param and isinstance(param['type'], object): + param['type'] = param['type'].__name__ + desc['return_data'] = return_data + desc.pop('outputs') + if not desc.get('required'): + desc['required'] = [param['name'] for param in desc['parameters']] + api_desc.append(desc) + if len(api_list) > 1: + tool_description = dict( + name=type, + api_list=api_desc + ) + self.name2func = {f'{api.name}': api for api in api_list} + else: + tool_description = api_desc[0] + self.name2func = {'run': api_list[0]} + super().__init__( + description=tool_description, + parser=parser, + enable=enable + ) + + @property + def is_toolkit(self): + return 'api_list' in self.description + + def __call__(self, inputs: str, name='run') -> ActionReturn: + fallback_args = {'inputs': inputs, 'name': name} + if not self.name2func.get(name): + return ActionReturn( + fallback_args, + type=self.name, + errmsg=f'invalid API: {name}', + state=ActionStatusCode.API_ERROR) + try: + inputs = self._parser.parse_inputs(inputs, name) + except ParseError as exc: + return ActionReturn( + fallback_args, + type=self.name, + errmsg=exc.err_msg, + state=ActionStatusCode.ARGS_ERROR) + outputs = self.name2func.get(name)(**inputs) + try: + outputs = self.name2func.get(name)(**inputs) + except Exception as exc: + return ActionReturn( + inputs, + type=self.name, + errmsg=str(exc), + state=ActionStatusCode.API_ERROR) + if isinstance(outputs, ActionReturn): + action_return = outputs + if not action_return.args: + action_return.args = inputs + if not action_return.type: + action_return.type = self.name + else: + result = self._parser.parse_outputs(outputs) + action_return = ActionReturn(inputs, type=self.name, result=result) + return action_return + From deaca8322bf72bc335531d077e350ef809f66a5d Mon Sep 17 00:00:00 2001 From: liujiangning Date: Tue, 27 Feb 2024 20:52:07 +0800 Subject: [PATCH 02/13] refine code --- examples/internlm2_agent_web_demo.py | 1 + lagent/actions/agentlego_wrapper.py | 91 ++++++++++------------------ 2 files changed, 33 insertions(+), 59 deletions(-) diff --git a/examples/internlm2_agent_web_demo.py b/examples/internlm2_agent_web_demo.py index 6d729e33..c5b146ae 100644 --- a/examples/internlm2_agent_web_demo.py +++ b/examples/internlm2_agent_web_demo.py @@ -27,6 +27,7 @@ def init_state(self): # ArxivSearch(), AgentLegoToolkit(type='ImageDescription', url='http://127.0.0.1:16180/openapi.json'), AgentLegoToolkit(type='Calculator', url='http://127.0.0.1:16181/openapi.json') + # AgentLegoToolkit(type='PluginMarket', url='http://127.0.0.1:16182/openapi.json') ] st.session_state['plugin_map'] = { action.name: action diff --git a/lagent/actions/agentlego_wrapper.py b/lagent/actions/agentlego_wrapper.py index bf55cd3b..73ad6fb7 100644 --- a/lagent/actions/agentlego_wrapper.py +++ b/lagent/actions/agentlego_wrapper.py @@ -1,15 +1,14 @@ -from typing import List, Union, Optional +from typing import List, Optional +import types from copy import deepcopy -from lagent import BaseAction, ActionReturn, ActionStatusCode -from lagent.actions.parser import ParseError, JsonParser +from lagent import BaseAction +from lagent.actions.parser import JsonParser from agentlego.tools.remote import RemoteTool from agentlego.parsers import DefaultParser class AgentLegoToolkit(BaseAction): - """A wrapper to align with the interface of iLagent tools.""" - def __init__(self, type: str, url: Optional[str] = None, @@ -25,37 +24,25 @@ def __init__(self, else: assert spec_dict is not None spec = dict(spec_dict=spec_dict) - api_list = [api for api in RemoteTool.from_openapi(**spec)] + if url is not None and not url.endswith('.json'): + api_list = [RemoteTool(url)] + else: + api_list = [api for api in RemoteTool.from_openapi(**spec)] api_desc = [] for api in api_list: api.set_parser(DefaultParser) desc = deepcopy(api.toolmeta.__dict__) - if not desc.get('parameters') and desc.get('inputs'): - parameters = [vars(param) for param in desc['inputs']] - for param in parameters: - if 'type' in param and isinstance(param['type'], object): - param['type'] = param['type'].__name__ - desc['parameters'] = parameters - desc.pop('inputs') - if not desc.get('return_data') and desc.get('outputs'): - return_data = [vars(param) for param in desc['outputs']] - for param in return_data: - if 'type' in param and isinstance(param['type'], object): - param['type'] = param['type'].__name__ - desc['return_data'] = return_data - desc.pop('outputs') - if not desc.get('required'): - desc['required'] = [param['name'] for param in desc['parameters']] + description_normalization(desc) api_desc.append(desc) if len(api_list) > 1: tool_description = dict( name=type, api_list=api_desc ) - self.name2func = {f'{api.name}': api for api in api_list} + self.add_method(api_list) else: tool_description = api_desc[0] - self.name2func = {'run': api_list[0]} + setattr(self, 'run', api_list[0]) super().__init__( description=tool_description, parser=parser, @@ -66,39 +53,25 @@ def __init__(self, def is_toolkit(self): return 'api_list' in self.description - def __call__(self, inputs: str, name='run') -> ActionReturn: - fallback_args = {'inputs': inputs, 'name': name} - if not self.name2func.get(name): - return ActionReturn( - fallback_args, - type=self.name, - errmsg=f'invalid API: {name}', - state=ActionStatusCode.API_ERROR) - try: - inputs = self._parser.parse_inputs(inputs, name) - except ParseError as exc: - return ActionReturn( - fallback_args, - type=self.name, - errmsg=exc.err_msg, - state=ActionStatusCode.ARGS_ERROR) - outputs = self.name2func.get(name)(**inputs) - try: - outputs = self.name2func.get(name)(**inputs) - except Exception as exc: - return ActionReturn( - inputs, - type=self.name, - errmsg=str(exc), - state=ActionStatusCode.API_ERROR) - if isinstance(outputs, ActionReturn): - action_return = outputs - if not action_return.args: - action_return.args = inputs - if not action_return.type: - action_return.type = self.name - else: - result = self._parser.parse_outputs(outputs) - action_return = ActionReturn(inputs, type=self.name, result=result) - return action_return + def add_method(self, funcs): + for func in funcs: + setattr(self, func.name, func) + +def description_normalization(desc: dict): + if not desc.get('parameters') and desc.get('inputs'): + parameters = [vars(param) for param in desc['inputs']] + for param in parameters: + if 'type' in param and isinstance(param['type'], object): + param['type'] = param['type'].__name__ + desc['parameters'] = parameters + desc.pop('inputs') + if not desc.get('return_data') and desc.get('outputs'): + return_data = [vars(param) for param in desc['outputs']] + for param in return_data: + if 'type' in param and isinstance(param['type'], object): + param['type'] = param['type'].__name__ + desc['return_data'] = return_data + desc.pop('outputs') + if not desc.get('required'): + desc['required'] = [param['name'] for param in desc['parameters']] From de64e7ef7d46157e9c0926cee552f1546f21d99a Mon Sep 17 00:00:00 2001 From: liujiangning Date: Thu, 29 Feb 2024 16:39:08 +0800 Subject: [PATCH 03/13] To lagent --- lagent/actions/agentlego_wrapper.py | 36 +++++++++++++---------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/lagent/actions/agentlego_wrapper.py b/lagent/actions/agentlego_wrapper.py index 73ad6fb7..9544eec3 100644 --- a/lagent/actions/agentlego_wrapper.py +++ b/lagent/actions/agentlego_wrapper.py @@ -1,14 +1,14 @@ -from typing import List, Optional -import types -from copy import deepcopy -from lagent import BaseAction -from lagent.actions.parser import JsonParser +from typing import Optional +# from agentlego.parsers import DefaultParser from agentlego.tools.remote import RemoteTool -from agentlego.parsers import DefaultParser + +from lagent import BaseAction +from lagent.actions.parser import JsonParser class AgentLegoToolkit(BaseAction): + def __init__(self, type: str, url: Optional[str] = None, @@ -25,29 +25,25 @@ def __init__(self, assert spec_dict is not None spec = dict(spec_dict=spec_dict) if url is not None and not url.endswith('.json'): - api_list = [RemoteTool(url)] + api_list = [RemoteTool.from_url(url).to_lagent()] else: - api_list = [api for api in RemoteTool.from_openapi(**spec)] + api_list = [ + api.to_lagent() for api in RemoteTool.from_openapi(**spec) + ] api_desc = [] for api in api_list: - api.set_parser(DefaultParser) - desc = deepcopy(api.toolmeta.__dict__) - description_normalization(desc) - api_desc.append(desc) + # api.set_parser(DefaultParser) + # desc = deepcopy(api.toolmeta.__dict__) + # description_normalization(desc) + api_desc.append(api.description) if len(api_list) > 1: - tool_description = dict( - name=type, - api_list=api_desc - ) + tool_description = dict(name=type, api_list=api_desc) self.add_method(api_list) else: tool_description = api_desc[0] setattr(self, 'run', api_list[0]) super().__init__( - description=tool_description, - parser=parser, - enable=enable - ) + description=tool_description, parser=parser, enable=enable) @property def is_toolkit(self): From 02701c2eceb584d63d4092e6a55d45d70c0c9d55 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Thu, 29 Feb 2024 17:31:07 +0800 Subject: [PATCH 04/13] To lagent --- examples/internlm2_agent_web_demo.py | 15 +++++++++------ lagent/actions/agentlego_wrapper.py | 26 ++------------------------ 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/examples/internlm2_agent_web_demo.py b/examples/internlm2_agent_web_demo.py index c5b146ae..97d5b3a9 100644 --- a/examples/internlm2_agent_web_demo.py +++ b/examples/internlm2_agent_web_demo.py @@ -6,13 +6,12 @@ import streamlit as st from lagent.actions import ActionExecutor, ArxivSearch, IPythonInterpreter +from lagent.actions.agentlego_wrapper import AgentLegoToolkit from lagent.agents.internlm2_agent import INTERPRETER_CN, META_CN, PLUGIN_CN, Internlm2Agent, Internlm2Protocol from lagent.llms.lmdepoly_wrapper import LMDeployClient from lagent.llms.meta_template import INTERNLM2_META as META from lagent.schema import AgentStatusCode -from lagent.actions.agentlego_wrapper import AgentLegoToolkit - # from streamlit.logger import get_logger @@ -24,10 +23,14 @@ def init_state(self): st.session_state['user'] = [] action_list = [ - # ArxivSearch(), - AgentLegoToolkit(type='ImageDescription', url='http://127.0.0.1:16180/openapi.json'), - AgentLegoToolkit(type='Calculator', url='http://127.0.0.1:16181/openapi.json') - # AgentLegoToolkit(type='PluginMarket', url='http://127.0.0.1:16182/openapi.json') + ArxivSearch(), + AgentLegoToolkit( + type='ImageDescription', + url='http://127.0.0.1:16180/openapi.json'), + AgentLegoToolkit( + type='Calculator', url='http://127.0.0.1:16181/openapi.json'), + AgentLegoToolkit( + type='PluginMarket', url='http://127.0.0.1:16182/openapi.json') ] st.session_state['plugin_map'] = { action.name: action diff --git a/lagent/actions/agentlego_wrapper.py b/lagent/actions/agentlego_wrapper.py index 9544eec3..eb2d4e00 100644 --- a/lagent/actions/agentlego_wrapper.py +++ b/lagent/actions/agentlego_wrapper.py @@ -32,16 +32,13 @@ def __init__(self, ] api_desc = [] for api in api_list: - # api.set_parser(DefaultParser) - # desc = deepcopy(api.toolmeta.__dict__) - # description_normalization(desc) api_desc.append(api.description) if len(api_list) > 1: tool_description = dict(name=type, api_list=api_desc) self.add_method(api_list) else: tool_description = api_desc[0] - setattr(self, 'run', api_list[0]) + setattr(self, 'run', api_list[0].run) super().__init__( description=tool_description, parser=parser, enable=enable) @@ -51,23 +48,4 @@ def is_toolkit(self): def add_method(self, funcs): for func in funcs: - setattr(self, func.name, func) - - -def description_normalization(desc: dict): - if not desc.get('parameters') and desc.get('inputs'): - parameters = [vars(param) for param in desc['inputs']] - for param in parameters: - if 'type' in param and isinstance(param['type'], object): - param['type'] = param['type'].__name__ - desc['parameters'] = parameters - desc.pop('inputs') - if not desc.get('return_data') and desc.get('outputs'): - return_data = [vars(param) for param in desc['outputs']] - for param in return_data: - if 'type' in param and isinstance(param['type'], object): - param['type'] = param['type'].__name__ - desc['return_data'] = return_data - desc.pop('outputs') - if not desc.get('required'): - desc['required'] = [param['name'] for param in desc['parameters']] + setattr(self, func.name, func.run) From c66c0d6283d3c9e13503fadcd284f51e8d74fe63 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Fri, 1 Mar 2024 11:37:02 +0800 Subject: [PATCH 05/13] fix comments --- examples/internlm2_agent_web_demo.py | 13 ++----------- lagent/actions/agentlego_wrapper.py | 7 ++----- requirements/optional.txt | 1 + 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/examples/internlm2_agent_web_demo.py b/examples/internlm2_agent_web_demo.py index 97d5b3a9..e6af039a 100644 --- a/examples/internlm2_agent_web_demo.py +++ b/examples/internlm2_agent_web_demo.py @@ -6,7 +6,7 @@ import streamlit as st from lagent.actions import ActionExecutor, ArxivSearch, IPythonInterpreter -from lagent.actions.agentlego_wrapper import AgentLegoToolkit +# from lagent.actions.agentlego_wrapper import AgentLegoToolkit from lagent.agents.internlm2_agent import INTERPRETER_CN, META_CN, PLUGIN_CN, Internlm2Agent, Internlm2Protocol from lagent.llms.lmdepoly_wrapper import LMDeployClient from lagent.llms.meta_template import INTERNLM2_META as META @@ -22,16 +22,7 @@ def init_state(self): st.session_state['assistant'] = [] st.session_state['user'] = [] - action_list = [ - ArxivSearch(), - AgentLegoToolkit( - type='ImageDescription', - url='http://127.0.0.1:16180/openapi.json'), - AgentLegoToolkit( - type='Calculator', url='http://127.0.0.1:16181/openapi.json'), - AgentLegoToolkit( - type='PluginMarket', url='http://127.0.0.1:16182/openapi.json') - ] + action_list = [ArxivSearch()] st.session_state['plugin_map'] = { action.name: action for action in action_list diff --git a/lagent/actions/agentlego_wrapper.py b/lagent/actions/agentlego_wrapper.py index eb2d4e00..f4b25b93 100644 --- a/lagent/actions/agentlego_wrapper.py +++ b/lagent/actions/agentlego_wrapper.py @@ -35,7 +35,8 @@ def __init__(self, api_desc.append(api.description) if len(api_list) > 1: tool_description = dict(name=type, api_list=api_desc) - self.add_method(api_list) + for func in api_list: + setattr(self, func.name, func.run) else: tool_description = api_desc[0] setattr(self, 'run', api_list[0].run) @@ -45,7 +46,3 @@ def __init__(self, @property def is_toolkit(self): return 'api_list' in self.description - - def add_method(self, funcs): - for func in funcs: - setattr(self, func.name, func.run) diff --git a/requirements/optional.txt b/requirements/optional.txt index 0fe2504d..4b576507 100644 --- a/requirements/optional.txt +++ b/requirements/optional.txt @@ -1,3 +1,4 @@ +agentlego google-search-results lmdeploy>=0.2.3 pillow From a3e2822751a03fbca1b824c4bfbd34b94d487e22 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Mon, 4 Mar 2024 11:02:50 +0800 Subject: [PATCH 06/13] rename 'type' to 'name' --- lagent/actions/agentlego_wrapper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lagent/actions/agentlego_wrapper.py b/lagent/actions/agentlego_wrapper.py index f4b25b93..e4c98255 100644 --- a/lagent/actions/agentlego_wrapper.py +++ b/lagent/actions/agentlego_wrapper.py @@ -10,7 +10,7 @@ class AgentLegoToolkit(BaseAction): def __init__(self, - type: str, + name: str, url: Optional[str] = None, text: Optional[str] = None, spec_dict: Optional[dict] = None, @@ -34,7 +34,7 @@ def __init__(self, for api in api_list: api_desc.append(api.description) if len(api_list) > 1: - tool_description = dict(name=type, api_list=api_desc) + tool_description = dict(name=name, api_list=api_desc) for func in api_list: setattr(self, func.name, func.run) else: From e014aa5b85e8f07af552f5165ccced89ad2361ed Mon Sep 17 00:00:00 2001 From: liujiangning Date: Mon, 4 Mar 2024 13:55:24 +0800 Subject: [PATCH 07/13] test 7b model --- examples/internlm2_agent_web_demo.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/examples/internlm2_agent_web_demo.py b/examples/internlm2_agent_web_demo.py index e6af039a..2315335d 100644 --- a/examples/internlm2_agent_web_demo.py +++ b/examples/internlm2_agent_web_demo.py @@ -5,7 +5,7 @@ import streamlit as st -from lagent.actions import ActionExecutor, ArxivSearch, IPythonInterpreter +from lagent.actions import PPT, ActionExecutor, ArxivSearch, BINGMap, GoogleScholar, IPythonInterpreter # from lagent.actions.agentlego_wrapper import AgentLegoToolkit from lagent.agents.internlm2_agent import INTERPRETER_CN, META_CN, PLUGIN_CN, Internlm2Agent, Internlm2Protocol from lagent.llms.lmdepoly_wrapper import LMDeployClient @@ -22,7 +22,18 @@ def init_state(self): st.session_state['assistant'] = [] st.session_state['user'] = [] - action_list = [ArxivSearch()] + action_list = [ + ArxivSearch(), + PPT(), + BINGMap( + key= + 'qaTBEmz3VIVotIKfVFLO~B_F0RIwD8ZEljsKEgQopdQ~AmFcljtooPxkBsCRVVQMtIBNS9S-oSmS1MaAsPiBUVN1eds4UeNum4M3a6cYX-kg' + ), + GoogleScholar( + api_key= + 'a558de7dee10146326ca86fbaa0736bdd947c9e646cd3f14da5aff177d6b2ff0' + ) + ] st.session_state['plugin_map'] = { action.name: action for action in action_list From 6c5a37ebbd08e21d79fab854019337522729b47a Mon Sep 17 00:00:00 2001 From: liujiangning Date: Mon, 4 Mar 2024 13:55:47 +0800 Subject: [PATCH 08/13] test 7b model --- examples/internlm2_agent_web_demo.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/internlm2_agent_web_demo.py b/examples/internlm2_agent_web_demo.py index 2315335d..705da453 100644 --- a/examples/internlm2_agent_web_demo.py +++ b/examples/internlm2_agent_web_demo.py @@ -26,12 +26,10 @@ def init_state(self): ArxivSearch(), PPT(), BINGMap( - key= - 'qaTBEmz3VIVotIKfVFLO~B_F0RIwD8ZEljsKEgQopdQ~AmFcljtooPxkBsCRVVQMtIBNS9S-oSmS1MaAsPiBUVN1eds4UeNum4M3a6cYX-kg' + key='qaTBEmz3VIVotIKfVFLO~B_F0RIwD8ZEljsKEgQopdQ~AmFcljtooPxkBsCRVVQMtIBNS9S-oSmS1MaAsPiBUVN1eds4UeNum4M3a6cYX-kg' ), GoogleScholar( - api_key= - 'a558de7dee10146326ca86fbaa0736bdd947c9e646cd3f14da5aff177d6b2ff0' + api_key='a558de7dee10146326ca86fbaa0736bdd947c9e646cd3f14da5aff177d6b2ff0' ) ] st.session_state['plugin_map'] = { From d60881e69dee629cdb49aaa627832f676b553839 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Wed, 13 Mar 2024 15:58:10 +0800 Subject: [PATCH 09/13] Fix errmsg: cast dict to str --- lagent/agents/internlm2_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lagent/agents/internlm2_agent.py b/lagent/agents/internlm2_agent.py index 6c5f4e08..6e159420 100644 --- a/lagent/agents/internlm2_agent.py +++ b/lagent/agents/internlm2_agent.py @@ -182,7 +182,7 @@ def format_response(self, action_return, name) -> dict: if action_return.state == ActionStatusCode.SUCCESS: response = action_return.format_result() else: - response = action_return.errmsg + response = str(action_return.errmsg) content = self.execute['begin'] + response + self.execute['end'] if self.execute.get('fallback_role'): return dict( From ec00c7d54de6cdd85d0bc5757222f01148d7a412 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Thu, 14 Mar 2024 15:23:47 +0800 Subject: [PATCH 10/13] cast errmsg to type str in builtin_actions --- lagent/actions/builtin_actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lagent/actions/builtin_actions.py b/lagent/actions/builtin_actions.py index 33702132..f336bdf2 100644 --- a/lagent/actions/builtin_actions.py +++ b/lagent/actions/builtin_actions.py @@ -37,7 +37,7 @@ def run(self, err_msg: Optional[str] = None) -> ActionReturn: action_return = ActionReturn( url=None, args=dict(text=err_msg), - errmsg=err_msg or self._err_msg, + errmsg=str(err_msg) or self._err_msg, type=self.name, valid=ActionValidCode.INVALID, state=ActionStatusCode.API_ERROR) @@ -76,7 +76,7 @@ def run(self, err_msg: Optional[str] = None) -> ActionReturn: url=None, args=dict(text=err_msg), type=self.name, - errmsg=err_msg or self._err_msg, + errmsg=str(err_msg) or self._err_msg, valid=ActionValidCode.INVALID, state=ActionStatusCode.API_ERROR) return action_return From 24304acf8c7323c6f877db104f1e62f0df3cbcc7 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Thu, 11 Apr 2024 11:55:55 +0800 Subject: [PATCH 11/13] update --- examples/internlm2_agent_web_demo.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/internlm2_agent_web_demo.py b/examples/internlm2_agent_web_demo.py index 76d3e716..b37e31d4 100644 --- a/examples/internlm2_agent_web_demo.py +++ b/examples/internlm2_agent_web_demo.py @@ -25,12 +25,10 @@ def init_state(self): action_list = [ ArxivSearch(), PPT(), - BINGMap( - key='qaTBEmz3VIVotIKfVFLO~B_F0RIwD8ZEljsKEgQopdQ~AmFcljtooPxkBsCRVVQMtIBNS9S-oSmS1MaAsPiBUVN1eds4UeNum4M3a6cYX-kg' - ), - GoogleScholar( - api_key='a558de7dee10146326ca86fbaa0736bdd947c9e646cd3f14da5aff177d6b2ff0' - ) + BINGMap(key='Your api key' # noqa + ), + GoogleScholar(api_key='Your api key' # noqa + ) ] st.session_state['plugin_map'] = { action.name: action @@ -112,7 +110,7 @@ def setup_sidebar(self): actions=[IPythonInterpreter()]) else: st.session_state['chatbot']._interpreter_executor = None - st.session_state['chatbot']._protocol._meta_template = meta_prompt + st.session_state['chatbot']._protocol.meta_prompt = meta_prompt st.session_state['chatbot']._protocol.plugin_prompt = plugin_prompt st.session_state[ 'chatbot']._protocol.interpreter_prompt = da_prompt @@ -149,8 +147,8 @@ def initialize_chatbot(self, model, plugin_action): plugin='<|plugin|>', interpreter='<|interpreter|>'), belong='assistant', end='<|action_end|>\n', - ), ), - max_turn=7) + )), + max_turn=15) def render_user(self, prompt: str): with st.chat_message('user'): From ca2682ab86c2a3897468b852d48984cf82565624 Mon Sep 17 00:00:00 2001 From: liujiangning Date: Mon, 1 Jul 2024 18:02:10 +0800 Subject: [PATCH 12/13] update for mindSearch --- lagent/agents/internlm2_agent.py | 28 +++++++++++++++------- lagent/llms/openai.py | 40 ++++++++++++++++++-------------- lagent/schema.py | 8 +++++-- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/lagent/agents/internlm2_agent.py b/lagent/agents/internlm2_agent.py index 6e159420..ccf85a61 100644 --- a/lagent/agents/internlm2_agent.py +++ b/lagent/agents/internlm2_agent.py @@ -113,7 +113,6 @@ def format_plugin(message): else: new_message.append( dict(role=message['role'], content=new_content)) - return new_message def format(self, @@ -125,9 +124,10 @@ def format(self, if self.meta_prompt: formatted.append(dict(role='system', content=self.meta_prompt)) if interpreter_executor and self.interpreter_prompt: - interpreter_info = interpreter_executor.get_actions_info()[0] - interpreter_prompt = self.interpreter_prompt.format( - code_prompt=interpreter_info['description']) + # interpreter_info = interpreter_executor.get_actions_info()[0] + # interpreter_prompt = self.interpreter_prompt.format( + # code_prompt=interpreter_info['description']) + interpreter_prompt = self.interpreter_prompt formatted.append( dict( role='system', @@ -169,13 +169,23 @@ def parse(self, message, plugin_executor: ActionExecutor, action = action.split(self.tool['end'].strip())[0] return 'plugin', message, action if self.tool['name_map']['interpreter'] in message: - message, code = message.split( - f"{self.tool['start_token']}" - f"{self.tool['name_map']['interpreter']}") + try: + message, code, *_ = message.split( + f"{self.tool['start_token']}" + f"{self.tool['name_map']['interpreter']}") + # message, code, *_ = message.split(f"{self.tool['start_token']}") + # _, code, *_ = code.split(f"{self.tool['name_map']['interpreter']}") + except ValueError: + message, code, *_ = message.split( + self.tool['name_map']['interpreter']) + tool_start_idx = message.rfind(self.tool['start_token']) + if tool_start_idx != -1: + message = message[:tool_start_idx] + message = message.strip() code = code.split(self.tool['end'].strip())[0].strip() return 'interpreter', message, dict( - name=interpreter_executor.action_names()[0], - parameters=dict(command=code)) + name='IPythonInterpreter', parameters=dict( + command=code)) if interpreter_executor else None return None, message.split(self.tool['start_token'])[0], None def format_response(self, action_return, name) -> dict: diff --git a/lagent/llms/openai.py b/lagent/llms/openai.py index 91b3f236..1835a9d6 100644 --- a/lagent/llms/openai.py +++ b/lagent/llms/openai.py @@ -1,6 +1,7 @@ import json import os import time +import warnings from concurrent.futures import ThreadPoolExecutor from logging import getLogger from threading import Lock @@ -10,6 +11,8 @@ from .base_api import BaseAPIModel +warnings.simplefilter('default') + OPENAI_API_BASE = 'https://api.openai.com/v1/chat/completions' @@ -45,6 +48,7 @@ def __init__(self, model_type: str = 'gpt-3.5-turbo', query_per_second: int = 1, retry: int = 2, + json_mode: bool = False, key: Union[str, List[str]] = 'ENV', org: Optional[Union[str, List[str]]] = None, meta_template: Optional[Dict] = [ @@ -53,13 +57,19 @@ def __init__(self, dict(role='assistant', api_role='assistant') ], openai_api_base: str = OPENAI_API_BASE, + proxies: Optional[Dict] = None, **gen_params): + if 'top_k' in gen_params: + warnings.warn('`top_k` parameter is deprecated in OpenAI APIs.', + DeprecationWarning) + gen_params.pop('top_k') super().__init__( model_type=model_type, meta_template=meta_template, query_per_second=query_per_second, retry=retry, **gen_params) + self.gen_params.pop('top_k') self.logger = getLogger(__name__) if isinstance(key, str): @@ -79,16 +89,8 @@ def __init__(self, self.org_ctr = 0 self.url = openai_api_base self.model_type = model_type - - # max num token for gpt-3.5-turbo is 4097 - context_window = 4096 - if '32k' in self.model_type: - context_window = 32768 - elif '16k' in self.model_type: - context_window = 16384 - elif 'gpt-4' in self.model_type: - context_window = 8192 - self.context_window = context_window + self.proxies = proxies + self.json_mode = json_mode def chat( self, @@ -132,9 +134,7 @@ def _chat(self, messages: List[dict], **gen_params) -> str: gen_params = gen_params.copy() # Hold out 100 tokens due to potential errors in tiktoken calculation - max_tokens = min( - gen_params.pop('max_new_tokens'), - self.context_window - len(self.tokenize(str(input))) - 100) + max_tokens = min(gen_params.pop('max_new_tokens'), 4096) if max_tokens <= 0: return '' @@ -170,17 +170,23 @@ def _chat(self, messages: List[dict], **gen_params) -> str: header['OpenAI-Organization'] = self.orgs[self.org_ctr] try: + gen_params_new = gen_params.copy() data = dict( model=self.model_type, messages=messages, max_tokens=max_tokens, n=1, - stop=gen_params.pop('stop_words'), - frequency_penalty=gen_params.pop('repetition_penalty'), - **gen_params, + stop=gen_params_new.pop('stop_words'), + frequency_penalty=gen_params_new.pop('repetition_penalty'), + **gen_params_new, ) + if self.json_mode: + data['response_format'] = {'type': 'json_object'} raw_response = requests.post( - self.url, headers=header, data=json.dumps(data)) + self.url, + headers=header, + data=json.dumps(data), + proxies=self.proxies) except requests.ConnectionError: print('Got connection error, retrying...') continue diff --git a/lagent/schema.py b/lagent/schema.py index a7f8e0cd..86f608fd 100644 --- a/lagent/schema.py +++ b/lagent/schema.py @@ -1,6 +1,6 @@ from dataclasses import asdict, dataclass, field from enum import IntEnum -from typing import List, Optional, Union +from typing import Dict, List, Optional, Union def enum_dict_factory(inputs): @@ -77,12 +77,16 @@ class AgentStatusCode(IntEnum): CODING = 6 # start python CODE_END = 7 # end python CODE_RETURN = 8 # python return + ANSWER_ING = 9 # final answer is in streaming @dataclass class AgentReturn: + type: str = '' state: Union[AgentStatusCode, int] = AgentStatusCode.END actions: List[ActionReturn] = field(default_factory=list) response: str = '' inner_steps: List = field(default_factory=list) - errmsg: Optional[str] = None + nodes: Dict = None + adjacency_list: Dict = None + errmsg: Optional[str] = None \ No newline at end of file From 84fd0a6de52dc8349d7bd0b3d6a712e585dffeef Mon Sep 17 00:00:00 2001 From: liujiangning Date: Tue, 9 Jul 2024 19:11:57 +0800 Subject: [PATCH 13/13] Feat: stream chat for GPTAPI --- lagent/llms/openai.py | 47 +++++++++++++++++++++++++++++++++++++++---- lagent/schema.py | 3 ++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/lagent/llms/openai.py b/lagent/llms/openai.py index 1835a9d6..ffc87ba4 100644 --- a/lagent/llms/openai.py +++ b/lagent/llms/openai.py @@ -120,6 +120,27 @@ def chat( ret = [task.result() for task in tasks] return ret[0] if isinstance(inputs[0], dict) else ret + def stream_chat( + self, + inputs: List[dict], + **gen_params, + ) -> str: + """Generate responses given the contexts. + + Args: + inputs (List[dict]): a list of messages + gen_params: additional generation configuration + + Returns: + str: generated string + """ + assert isinstance(inputs, list) + if 'max_tokens' in gen_params: + raise NotImplementedError('unsupported parameter: max_tokens') + gen_params = {**self.gen_params, **gen_params} + gen_params['stream'] = True + yield from self._chat(inputs, **gen_params) + def _chat(self, messages: List[dict], **gen_params) -> str: """Generate completion from a list of templates. @@ -187,16 +208,32 @@ def _chat(self, messages: List[dict], **gen_params) -> str: headers=header, data=json.dumps(data), proxies=self.proxies) + if 'stream' not in data or not data['stream']: + response = raw_response.json() + return response['choices'][0]['message']['content'].strip() + else: + resp = '' + for chunk in raw_response.iter_lines( + chunk_size=8192, decode_unicode=False, + delimiter=b'\n'): + if chunk: + decoded = chunk.decode('utf-8') + if decoded == 'data: [DONE]': + return + if decoded[:6] == 'data: ': + decoded = decoded[6:] + response = json.loads(decoded) + choice = response['choices'][0] + if choice['finish_reason'] == 'stop': + return + resp += choice['delta']['content'].strip() + yield resp except requests.ConnectionError: print('Got connection error, retrying...') continue - try: - response = raw_response.json() except requests.JSONDecodeError: print('JsonDecode error, got', str(raw_response.content)) continue - try: - return response['choices'][0]['message']['content'].strip() except KeyError: if 'error' in response: if response['error']['code'] == 'rate_limit_exceeded': @@ -209,6 +246,8 @@ def _chat(self, messages: List[dict], **gen_params) -> str: print('Find error message in response: ', str(response['error'])) + except Exception as error: + print(str(error)) max_num_retries += 1 raise RuntimeError('Calling OpenAI failed after retrying for ' diff --git a/lagent/schema.py b/lagent/schema.py index 86f608fd..94bcf370 100644 --- a/lagent/schema.py +++ b/lagent/schema.py @@ -83,10 +83,11 @@ class AgentStatusCode(IntEnum): @dataclass class AgentReturn: type: str = '' + content: str = '' state: Union[AgentStatusCode, int] = AgentStatusCode.END actions: List[ActionReturn] = field(default_factory=list) response: str = '' inner_steps: List = field(default_factory=list) nodes: Dict = None adjacency_list: Dict = None - errmsg: Optional[str] = None \ No newline at end of file + errmsg: Optional[str] = None