From daea357940ede271faa161dd8a16dee98f3a894c Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Thu, 25 Sep 2025 14:11:09 +0800 Subject: [PATCH] Fix: invalid COMPONENT_EXEC_TIMEOUT (#10278) ### What problem does this PR solve? Fix invalid COMPONENT_EXEC_TIMEOUT. #10273 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- agent/component/categorize.py | 8 ++++---- agent/component/string_transform.py | 2 +- agent/tools/arxiv.py | 6 +++--- agent/tools/code_exec.py | 2 +- agent/tools/duckduckgo.py | 6 +++--- agent/tools/email.py | 6 +++--- agent/tools/exesql.py | 2 +- agent/tools/github.py | 4 ++-- agent/tools/google.py | 6 +++--- agent/tools/googlescholar.py | 4 ++-- agent/tools/pubmed.py | 6 +++--- agent/tools/retrieval.py | 12 ++++++------ agent/tools/searxng.py | 21 ++++++++------------- agent/tools/tavily.py | 10 +++++----- agent/tools/wencai.py | 4 ++-- agent/tools/wikipedia.py | 6 +++--- agent/tools/yahoofinance.py | 4 ++-- rag/llm/chat_model.py | 8 ++++---- 18 files changed, 56 insertions(+), 61 deletions(-) diff --git a/agent/component/categorize.py b/agent/component/categorize.py index 3f1188363d..af2666fcbf 100644 --- a/agent/component/categorize.py +++ b/agent/component/categorize.py @@ -80,7 +80,7 @@ Here's description of each category: - Prioritize the most specific applicable category - Return only the category name without explanations - Use "Other" only when no other category fits - + """.format( "\n - ".join(list(self.category_description.keys())), "\n".join(descriptions) @@ -96,7 +96,7 @@ Here's description of each category: class Categorize(LLM, ABC): component_name = "Categorize" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60))) def _invoke(self, **kwargs): msg = self._canvas.get_history(self._param.message_history_window_size) if not msg: @@ -112,7 +112,7 @@ class Categorize(LLM, ABC): user_prompt = """ ---- Real Data ---- -{} → +{} → """.format(" | ".join(["{}: \"{}\"".format(c["role"].upper(), re.sub(r"\n", "", c["content"], flags=re.DOTALL)) for c in msg])) ans = chat_mdl.chat(self._param.sys_prompt, [{"role": "user", "content": user_prompt}], self._param.gen_conf()) logging.info(f"input: {user_prompt}, answer: {str(ans)}") @@ -134,4 +134,4 @@ class Categorize(LLM, ABC): self.set_output("_next", cpn_ids) def thoughts(self) -> str: - return "Which should it falls into {}? ...".format(",".join([f"`{c}`" for c, _ in self._param.category_description.items()])) \ No newline at end of file + return "Which should it falls into {}? ...".format(",".join([f"`{c}`" for c, _ in self._param.category_description.items()])) diff --git a/agent/component/string_transform.py b/agent/component/string_transform.py index 6ef6ba7094..fe812c0a81 100644 --- a/agent/component/string_transform.py +++ b/agent/component/string_transform.py @@ -56,7 +56,7 @@ class StringTransform(Message, ABC): "type": "line" } for k, o in self.get_input_elements_from_text(self._param.script).items()} - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60))) def _invoke(self, **kwargs): if self._param.method == "split": self._split(kwargs.get("line")) diff --git a/agent/tools/arxiv.py b/agent/tools/arxiv.py index be9715cc59..616afa31a4 100644 --- a/agent/tools/arxiv.py +++ b/agent/tools/arxiv.py @@ -61,7 +61,7 @@ class ArXivParam(ToolParamBase): class ArXiv(ToolBase, ABC): component_name = "ArXiv" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", "") @@ -97,6 +97,6 @@ class ArXiv(ToolBase, ABC): def thoughts(self) -> str: return """ -Keywords: {} +Keywords: {} Looking for the most relevant articles. - """.format(self.get_input().get("query", "-_-!")) \ No newline at end of file + """.format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/code_exec.py b/agent/tools/code_exec.py index b94dc8d5ec..f7dd6f753f 100644 --- a/agent/tools/code_exec.py +++ b/agent/tools/code_exec.py @@ -129,7 +129,7 @@ module.exports = { main }; class CodeExec(ToolBase, ABC): component_name = "CodeExec" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60))) def _invoke(self, **kwargs): lang = kwargs.get("lang", self._param.lang) script = kwargs.get("script", self._param.script) diff --git a/agent/tools/duckduckgo.py b/agent/tools/duckduckgo.py index 34c2e66ecc..0315d69713 100644 --- a/agent/tools/duckduckgo.py +++ b/agent/tools/duckduckgo.py @@ -73,7 +73,7 @@ class DuckDuckGoParam(ToolParamBase): class DuckDuckGo(ToolBase, ABC): component_name = "DuckDuckGo" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", "") @@ -115,6 +115,6 @@ class DuckDuckGo(ToolBase, ABC): def thoughts(self) -> str: return """ -Keywords: {} +Keywords: {} Looking for the most relevant articles. - """.format(self.get_input().get("query", "-_-!")) \ No newline at end of file + """.format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/email.py b/agent/tools/email.py index e9f6eaed82..ab6cc6ea63 100644 --- a/agent/tools/email.py +++ b/agent/tools/email.py @@ -98,8 +98,8 @@ class EmailParam(ToolParamBase): class Email(ToolBase, ABC): component_name = "Email" - - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)) + + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))) def _invoke(self, **kwargs): if not kwargs.get("to_email"): self.set_output("success", False) @@ -212,4 +212,4 @@ class Email(ToolBase, ABC): To: {} Subject: {} Your email is on its way—sit tight! -""".format(inputs.get("to_email", "-_-!"), inputs.get("subject", "-_-!")) \ No newline at end of file +""".format(inputs.get("to_email", "-_-!"), inputs.get("subject", "-_-!")) diff --git a/agent/tools/exesql.py b/agent/tools/exesql.py index c4bc4fdb40..aa2f723e9a 100644 --- a/agent/tools/exesql.py +++ b/agent/tools/exesql.py @@ -78,7 +78,7 @@ class ExeSQLParam(ToolParamBase): class ExeSQL(ToolBase, ABC): component_name = "ExeSQL" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))) def _invoke(self, **kwargs): def convert_decimals(obj): diff --git a/agent/tools/github.py b/agent/tools/github.py index d19a434b69..27cb1e3463 100644 --- a/agent/tools/github.py +++ b/agent/tools/github.py @@ -57,7 +57,7 @@ class GitHubParam(ToolParamBase): class GitHub(ToolBase, ABC): component_name = "GitHub" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", "") @@ -88,4 +88,4 @@ class GitHub(ToolBase, ABC): assert False, self.output() def thoughts(self) -> str: - return "Scanning GitHub repos related to `{}`.".format(self.get_input().get("query", "-_-!")) \ No newline at end of file + return "Scanning GitHub repos related to `{}`.".format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/google.py b/agent/tools/google.py index f68b51f910..455038abed 100644 --- a/agent/tools/google.py +++ b/agent/tools/google.py @@ -116,7 +116,7 @@ class GoogleParam(ToolParamBase): class Google(ToolBase, ABC): component_name = "Google" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("q"): self.set_output("formalized_content", "") @@ -154,6 +154,6 @@ class Google(ToolBase, ABC): def thoughts(self) -> str: return """ -Keywords: {} +Keywords: {} Looking for the most relevant articles. - """.format(self.get_input().get("query", "-_-!")) \ No newline at end of file + """.format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/googlescholar.py b/agent/tools/googlescholar.py index cfc32d63ec..bf906da4b9 100644 --- a/agent/tools/googlescholar.py +++ b/agent/tools/googlescholar.py @@ -63,7 +63,7 @@ class GoogleScholarParam(ToolParamBase): class GoogleScholar(ToolBase, ABC): component_name = "GoogleScholar" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", "") @@ -93,4 +93,4 @@ class GoogleScholar(ToolBase, ABC): assert False, self.output() def thoughts(self) -> str: - return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!")) \ No newline at end of file + return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/pubmed.py b/agent/tools/pubmed.py index c939b2fab5..6dce92a9be 100644 --- a/agent/tools/pubmed.py +++ b/agent/tools/pubmed.py @@ -33,7 +33,7 @@ class PubMedParam(ToolParamBase): self.meta:ToolMeta = { "name": "pubmed_search", "description": """ -PubMed is an openly accessible, free database which includes primarily the MEDLINE database of references and abstracts on life sciences and biomedical topics. +PubMed is an openly accessible, free database which includes primarily the MEDLINE database of references and abstracts on life sciences and biomedical topics. In addition to MEDLINE, PubMed provides access to: - older references from the print version of Index Medicus, back to 1951 and earlier - references to some journals before they were indexed in Index Medicus and MEDLINE, for instance Science, BMJ, and Annals of Surgery @@ -69,7 +69,7 @@ In addition to MEDLINE, PubMed provides access to: class PubMed(ToolBase, ABC): component_name = "PubMed" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", "") @@ -105,4 +105,4 @@ class PubMed(ToolBase, ABC): assert False, self.output() def thoughts(self) -> str: - return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!")) \ No newline at end of file + return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/retrieval.py b/agent/tools/retrieval.py index d6b0213c12..24370f1cab 100644 --- a/agent/tools/retrieval.py +++ b/agent/tools/retrieval.py @@ -74,7 +74,7 @@ class RetrievalParam(ToolParamBase): class Retrieval(ToolBase, ABC): component_name = "Retrieval" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", self._param.empty_response) @@ -164,18 +164,18 @@ class Retrieval(ToolBase, ABC): # Format the chunks for JSON output (similar to how other tools do it) json_output = kbinfos["chunks"].copy() - + self._canvas.add_reference(kbinfos["chunks"], kbinfos["doc_aggs"]) form_cnt = "\n".join(kb_prompt(kbinfos, 200000, True)) - + # Set both formalized content and JSON output self.set_output("formalized_content", form_cnt) self.set_output("json", json_output) - + return form_cnt def thoughts(self) -> str: return """ -Keywords: {} +Keywords: {} Looking for the most relevant articles. - """.format(self.get_input().get("query", "-_-!")) \ No newline at end of file + """.format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/searxng.py b/agent/tools/searxng.py index 25a8c0e46d..f8c30bfd1a 100644 --- a/agent/tools/searxng.py +++ b/agent/tools/searxng.py @@ -77,7 +77,7 @@ class SearXNGParam(ToolParamBase): class SearXNG(ToolBase, ABC): component_name = "SearXNG" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): # Gracefully handle try-run without inputs query = kwargs.get("query") @@ -94,7 +94,6 @@ class SearXNG(ToolBase, ABC): last_e = "" for _ in range(self._param.max_retries+1): try: - # 构建搜索参数 search_params = { 'q': query, 'format': 'json', @@ -104,33 +103,29 @@ class SearXNG(ToolBase, ABC): 'pageno': 1 } - # 发送搜索请求 response = requests.get( f"{searxng_url}/search", params=search_params, timeout=10 ) response.raise_for_status() - + data = response.json() - - # 验证响应数据 + if not data or not isinstance(data, dict): raise ValueError("Invalid response from SearXNG") - + results = data.get("results", []) if not isinstance(results, list): raise ValueError("Invalid results format from SearXNG") - - # 限制结果数量 + results = results[:self._param.top_n] - - # 处理搜索结果 + self._retrieve_chunks(results, get_title=lambda r: r.get("title", ""), get_url=lambda r: r.get("url", ""), get_content=lambda r: r.get("content", "")) - + self.set_output("json", results) return self.output("formalized_content") @@ -151,6 +146,6 @@ class SearXNG(ToolBase, ABC): def thoughts(self) -> str: return """ -Keywords: {} +Keywords: {} Searching with SearXNG for relevant results... """.format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/tavily.py b/agent/tools/tavily.py index fa9a266ab0..80203feec4 100644 --- a/agent/tools/tavily.py +++ b/agent/tools/tavily.py @@ -31,7 +31,7 @@ class TavilySearchParam(ToolParamBase): self.meta:ToolMeta = { "name": "tavily_search", "description": """ -Tavily is a search engine optimized for LLMs, aimed at efficient, quick and persistent search results. +Tavily is a search engine optimized for LLMs, aimed at efficient, quick and persistent search results. When searching: - Start with specific query which should focus on just a single aspect. - Number of keywords in query should be less than 5. @@ -101,7 +101,7 @@ When searching: class TavilySearch(ToolBase, ABC): component_name = "TavilySearch" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", "") @@ -136,7 +136,7 @@ class TavilySearch(ToolBase, ABC): def thoughts(self) -> str: return """ -Keywords: {} +Keywords: {} Looking for the most relevant articles. """.format(self.get_input().get("query", "-_-!")) @@ -199,7 +199,7 @@ class TavilyExtractParam(ToolParamBase): class TavilyExtract(ToolBase, ABC): component_name = "TavilyExtract" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60))) def _invoke(self, **kwargs): self.tavily_client = TavilyClient(api_key=self._param.api_key) last_e = None @@ -224,4 +224,4 @@ class TavilyExtract(ToolBase, ABC): assert False, self.output() def thoughts(self) -> str: - return "Opened {}—pulling out the main text…".format(self.get_input().get("urls", "-_-!")) \ No newline at end of file + return "Opened {}—pulling out the main text…".format(self.get_input().get("urls", "-_-!")) diff --git a/agent/tools/wencai.py b/agent/tools/wencai.py index 66213a08b3..e2f8adefcc 100644 --- a/agent/tools/wencai.py +++ b/agent/tools/wencai.py @@ -68,7 +68,7 @@ fund selection platform: through AI technology, is committed to providing excell class WenCai(ToolBase, ABC): component_name = "WenCai" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("report", "") @@ -111,4 +111,4 @@ class WenCai(ToolBase, ABC): assert False, self.output() def thoughts(self) -> str: - return "Pulling live financial data for `{}`.".format(self.get_input().get("query", "-_-!")) \ No newline at end of file + return "Pulling live financial data for `{}`.".format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/wikipedia.py b/agent/tools/wikipedia.py index 93bb6cfc07..83e3b13a8c 100644 --- a/agent/tools/wikipedia.py +++ b/agent/tools/wikipedia.py @@ -64,7 +64,7 @@ class WikipediaParam(ToolParamBase): class Wikipedia(ToolBase, ABC): component_name = "Wikipedia" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))) def _invoke(self, **kwargs): if not kwargs.get("query"): self.set_output("formalized_content", "") @@ -99,6 +99,6 @@ class Wikipedia(ToolBase, ABC): def thoughts(self) -> str: return """ -Keywords: {} +Keywords: {} Looking for the most relevant articles. - """.format(self.get_input().get("query", "-_-!")) \ No newline at end of file + """.format(self.get_input().get("query", "-_-!")) diff --git a/agent/tools/yahoofinance.py b/agent/tools/yahoofinance.py index 6e2dc0e62b..9feea20af8 100644 --- a/agent/tools/yahoofinance.py +++ b/agent/tools/yahoofinance.py @@ -72,7 +72,7 @@ class YahooFinanceParam(ToolParamBase): class YahooFinance(ToolBase, ABC): component_name = "YahooFinance" - @timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)) + @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60))) def _invoke(self, **kwargs): if not kwargs.get("stock_code"): self.set_output("report", "") @@ -111,4 +111,4 @@ class YahooFinance(ToolBase, ABC): assert False, self.output() def thoughts(self) -> str: - return "Pulling live financial data for `{}`.".format(self.get_input().get("stock_code", "-_-!")) \ No newline at end of file + return "Pulling live financial data for `{}`.".format(self.get_input().get("stock_code", "-_-!")) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index c966be2f73..e1d3dcf011 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -143,7 +143,7 @@ class Base(ABC): logging.info("[HISTORY]" + json.dumps(history, ensure_ascii=False, indent=2)) if self.model_name.lower().find("qwen3") >= 0: kwargs["extra_body"] = {"enable_thinking": False} - + response = self.client.chat.completions.create(model=self.model_name, messages=history, **gen_conf, **kwargs) if (not response.choices or not response.choices[0].message or not response.choices[0].message.content): @@ -156,12 +156,12 @@ class Base(ABC): def _chat_streamly(self, history, gen_conf, **kwargs): logging.info("[HISTORY STREAMLY]" + json.dumps(history, ensure_ascii=False, indent=4)) reasoning_start = False - + if kwargs.get("stop") or "stop" in gen_conf: response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf, stop=kwargs.get("stop")) else: response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf) - + for resp in response: if not resp.choices: continue @@ -643,7 +643,7 @@ class ZhipuChat(Base): del gen_conf["max_tokens"] gen_conf = self._clean_conf_plealty(gen_conf) return gen_conf - + def _clean_conf_plealty(self, gen_conf): if "presence_penalty" in gen_conf: del gen_conf["presence_penalty"]