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 3f1188363..af2666fcb 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 6ef6ba709..fe812c0a8 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 be9715cc5..616afa31a 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 b94dc8d5e..f7dd6f753 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 34c2e66ec..0315d6971 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 e9f6eaed8..ab6cc6ea6 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 c4bc4fdb4..aa2f723e9 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 d19a434b6..27cb1e346 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 f68b51f91..455038abe 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 cfc32d63e..bf906da4b 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 c939b2fab..6dce92a9b 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 d6b0213c1..24370f1ca 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 25a8c0e46..f8c30bfd1 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 fa9a266ab..80203feec 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 66213a08b..e2f8adefc 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 93bb6cfc0..83e3b13a8 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 6e2dc0e62..9feea20af 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 c966be2f7..e1d3dcf01 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"]