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)
This commit is contained in:
Yongteng Lei
2025-09-25 14:11:09 +08:00
committed by GitHub
parent 4aa1abd8e5
commit daea357940
18 changed files with 56 additions and 61 deletions

View File

@ -80,7 +80,7 @@ Here's description of each category:
- Prioritize the most specific applicable category - Prioritize the most specific applicable category
- Return only the category name without explanations - Return only the category name without explanations
- Use "Other" only when no other category fits - Use "Other" only when no other category fits
""".format( """.format(
"\n - ".join(list(self.category_description.keys())), "\n - ".join(list(self.category_description.keys())),
"\n".join(descriptions) "\n".join(descriptions)
@ -96,7 +96,7 @@ Here's description of each category:
class Categorize(LLM, ABC): class Categorize(LLM, ABC):
component_name = "Categorize" 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): def _invoke(self, **kwargs):
msg = self._canvas.get_history(self._param.message_history_window_size) msg = self._canvas.get_history(self._param.message_history_window_size)
if not msg: if not msg:
@ -112,7 +112,7 @@ class Categorize(LLM, ABC):
user_prompt = """ user_prompt = """
---- Real Data ---- ---- Real Data ----
{} {}
""".format(" | ".join(["{}: \"{}\"".format(c["role"].upper(), re.sub(r"\n", "", c["content"], flags=re.DOTALL)) for c in msg])) """.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()) 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)}") logging.info(f"input: {user_prompt}, answer: {str(ans)}")
@ -134,4 +134,4 @@ class Categorize(LLM, ABC):
self.set_output("_next", cpn_ids) self.set_output("_next", cpn_ids)
def thoughts(self) -> str: def thoughts(self) -> str:
return "Which should it falls into {}? ...".format(",".join([f"`{c}`" for c, _ in self._param.category_description.items()])) return "Which should it falls into {}? ...".format(",".join([f"`{c}`" for c, _ in self._param.category_description.items()]))

View File

@ -56,7 +56,7 @@ class StringTransform(Message, ABC):
"type": "line" "type": "line"
} for k, o in self.get_input_elements_from_text(self._param.script).items()} } 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): def _invoke(self, **kwargs):
if self._param.method == "split": if self._param.method == "split":
self._split(kwargs.get("line")) self._split(kwargs.get("line"))

View File

@ -61,7 +61,7 @@ class ArXivParam(ToolParamBase):
class ArXiv(ToolBase, ABC): class ArXiv(ToolBase, ABC):
component_name = "ArXiv" component_name = "ArXiv"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -97,6 +97,6 @@ class ArXiv(ToolBase, ABC):
def thoughts(self) -> str: def thoughts(self) -> str:
return """ return """
Keywords: {} Keywords: {}
Looking for the most relevant articles. Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!")) """.format(self.get_input().get("query", "-_-!"))

View File

@ -129,7 +129,7 @@ module.exports = { main };
class CodeExec(ToolBase, ABC): class CodeExec(ToolBase, ABC):
component_name = "CodeExec" 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): def _invoke(self, **kwargs):
lang = kwargs.get("lang", self._param.lang) lang = kwargs.get("lang", self._param.lang)
script = kwargs.get("script", self._param.script) script = kwargs.get("script", self._param.script)

View File

@ -73,7 +73,7 @@ class DuckDuckGoParam(ToolParamBase):
class DuckDuckGo(ToolBase, ABC): class DuckDuckGo(ToolBase, ABC):
component_name = "DuckDuckGo" component_name = "DuckDuckGo"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -115,6 +115,6 @@ class DuckDuckGo(ToolBase, ABC):
def thoughts(self) -> str: def thoughts(self) -> str:
return """ return """
Keywords: {} Keywords: {}
Looking for the most relevant articles. Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!")) """.format(self.get_input().get("query", "-_-!"))

View File

@ -98,8 +98,8 @@ class EmailParam(ToolParamBase):
class Email(ToolBase, ABC): class Email(ToolBase, ABC):
component_name = "Email" component_name = "Email"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("to_email"): if not kwargs.get("to_email"):
self.set_output("success", False) self.set_output("success", False)
@ -212,4 +212,4 @@ class Email(ToolBase, ABC):
To: {} To: {}
Subject: {} Subject: {}
Your email is on its way—sit tight! Your email is on its way—sit tight!
""".format(inputs.get("to_email", "-_-!"), inputs.get("subject", "-_-!")) """.format(inputs.get("to_email", "-_-!"), inputs.get("subject", "-_-!"))

View File

@ -78,7 +78,7 @@ class ExeSQLParam(ToolParamBase):
class ExeSQL(ToolBase, ABC): class ExeSQL(ToolBase, ABC):
component_name = "ExeSQL" 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 _invoke(self, **kwargs):
def convert_decimals(obj): def convert_decimals(obj):

View File

@ -57,7 +57,7 @@ class GitHubParam(ToolParamBase):
class GitHub(ToolBase, ABC): class GitHub(ToolBase, ABC):
component_name = "GitHub" component_name = "GitHub"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -88,4 +88,4 @@ class GitHub(ToolBase, ABC):
assert False, self.output() assert False, self.output()
def thoughts(self) -> str: def thoughts(self) -> str:
return "Scanning GitHub repos related to `{}`.".format(self.get_input().get("query", "-_-!")) return "Scanning GitHub repos related to `{}`.".format(self.get_input().get("query", "-_-!"))

View File

@ -116,7 +116,7 @@ class GoogleParam(ToolParamBase):
class Google(ToolBase, ABC): class Google(ToolBase, ABC):
component_name = "Google" component_name = "Google"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("q"): if not kwargs.get("q"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -154,6 +154,6 @@ class Google(ToolBase, ABC):
def thoughts(self) -> str: def thoughts(self) -> str:
return """ return """
Keywords: {} Keywords: {}
Looking for the most relevant articles. Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!")) """.format(self.get_input().get("query", "-_-!"))

View File

@ -63,7 +63,7 @@ class GoogleScholarParam(ToolParamBase):
class GoogleScholar(ToolBase, ABC): class GoogleScholar(ToolBase, ABC):
component_name = "GoogleScholar" component_name = "GoogleScholar"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -93,4 +93,4 @@ class GoogleScholar(ToolBase, ABC):
assert False, self.output() assert False, self.output()
def thoughts(self) -> str: def thoughts(self) -> str:
return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!")) return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!"))

View File

@ -33,7 +33,7 @@ class PubMedParam(ToolParamBase):
self.meta:ToolMeta = { self.meta:ToolMeta = {
"name": "pubmed_search", "name": "pubmed_search",
"description": """ "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: In addition to MEDLINE, PubMed provides access to:
- older references from the print version of Index Medicus, back to 1951 and earlier - 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 - 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): class PubMed(ToolBase, ABC):
component_name = "PubMed" component_name = "PubMed"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -105,4 +105,4 @@ class PubMed(ToolBase, ABC):
assert False, self.output() assert False, self.output()
def thoughts(self) -> str: def thoughts(self) -> str:
return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!")) return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!"))

View File

@ -74,7 +74,7 @@ class RetrievalParam(ToolParamBase):
class Retrieval(ToolBase, ABC): class Retrieval(ToolBase, ABC):
component_name = "Retrieval" component_name = "Retrieval"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", self._param.empty_response) 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) # Format the chunks for JSON output (similar to how other tools do it)
json_output = kbinfos["chunks"].copy() json_output = kbinfos["chunks"].copy()
self._canvas.add_reference(kbinfos["chunks"], kbinfos["doc_aggs"]) self._canvas.add_reference(kbinfos["chunks"], kbinfos["doc_aggs"])
form_cnt = "\n".join(kb_prompt(kbinfos, 200000, True)) form_cnt = "\n".join(kb_prompt(kbinfos, 200000, True))
# Set both formalized content and JSON output # Set both formalized content and JSON output
self.set_output("formalized_content", form_cnt) self.set_output("formalized_content", form_cnt)
self.set_output("json", json_output) self.set_output("json", json_output)
return form_cnt return form_cnt
def thoughts(self) -> str: def thoughts(self) -> str:
return """ return """
Keywords: {} Keywords: {}
Looking for the most relevant articles. Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!")) """.format(self.get_input().get("query", "-_-!"))

View File

@ -77,7 +77,7 @@ class SearXNGParam(ToolParamBase):
class SearXNG(ToolBase, ABC): class SearXNG(ToolBase, ABC):
component_name = "SearXNG" component_name = "SearXNG"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
# Gracefully handle try-run without inputs # Gracefully handle try-run without inputs
query = kwargs.get("query") query = kwargs.get("query")
@ -94,7 +94,6 @@ class SearXNG(ToolBase, ABC):
last_e = "" last_e = ""
for _ in range(self._param.max_retries+1): for _ in range(self._param.max_retries+1):
try: try:
# 构建搜索参数
search_params = { search_params = {
'q': query, 'q': query,
'format': 'json', 'format': 'json',
@ -104,33 +103,29 @@ class SearXNG(ToolBase, ABC):
'pageno': 1 'pageno': 1
} }
# 发送搜索请求
response = requests.get( response = requests.get(
f"{searxng_url}/search", f"{searxng_url}/search",
params=search_params, params=search_params,
timeout=10 timeout=10
) )
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
# 验证响应数据
if not data or not isinstance(data, dict): if not data or not isinstance(data, dict):
raise ValueError("Invalid response from SearXNG") raise ValueError("Invalid response from SearXNG")
results = data.get("results", []) results = data.get("results", [])
if not isinstance(results, list): if not isinstance(results, list):
raise ValueError("Invalid results format from SearXNG") raise ValueError("Invalid results format from SearXNG")
# 限制结果数量
results = results[:self._param.top_n] results = results[:self._param.top_n]
# 处理搜索结果
self._retrieve_chunks(results, self._retrieve_chunks(results,
get_title=lambda r: r.get("title", ""), get_title=lambda r: r.get("title", ""),
get_url=lambda r: r.get("url", ""), get_url=lambda r: r.get("url", ""),
get_content=lambda r: r.get("content", "")) get_content=lambda r: r.get("content", ""))
self.set_output("json", results) self.set_output("json", results)
return self.output("formalized_content") return self.output("formalized_content")
@ -151,6 +146,6 @@ class SearXNG(ToolBase, ABC):
def thoughts(self) -> str: def thoughts(self) -> str:
return """ return """
Keywords: {} Keywords: {}
Searching with SearXNG for relevant results... Searching with SearXNG for relevant results...
""".format(self.get_input().get("query", "-_-!")) """.format(self.get_input().get("query", "-_-!"))

View File

@ -31,7 +31,7 @@ class TavilySearchParam(ToolParamBase):
self.meta:ToolMeta = { self.meta:ToolMeta = {
"name": "tavily_search", "name": "tavily_search",
"description": """ "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: When searching:
- Start with specific query which should focus on just a single aspect. - Start with specific query which should focus on just a single aspect.
- Number of keywords in query should be less than 5. - Number of keywords in query should be less than 5.
@ -101,7 +101,7 @@ When searching:
class TavilySearch(ToolBase, ABC): class TavilySearch(ToolBase, ABC):
component_name = "TavilySearch" component_name = "TavilySearch"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -136,7 +136,7 @@ class TavilySearch(ToolBase, ABC):
def thoughts(self) -> str: def thoughts(self) -> str:
return """ return """
Keywords: {} Keywords: {}
Looking for the most relevant articles. Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!")) """.format(self.get_input().get("query", "-_-!"))
@ -199,7 +199,7 @@ class TavilyExtractParam(ToolParamBase):
class TavilyExtract(ToolBase, ABC): class TavilyExtract(ToolBase, ABC):
component_name = "TavilyExtract" 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): def _invoke(self, **kwargs):
self.tavily_client = TavilyClient(api_key=self._param.api_key) self.tavily_client = TavilyClient(api_key=self._param.api_key)
last_e = None last_e = None
@ -224,4 +224,4 @@ class TavilyExtract(ToolBase, ABC):
assert False, self.output() assert False, self.output()
def thoughts(self) -> str: def thoughts(self) -> str:
return "Opened {}—pulling out the main text…".format(self.get_input().get("urls", "-_-!")) return "Opened {}—pulling out the main text…".format(self.get_input().get("urls", "-_-!"))

View File

@ -68,7 +68,7 @@ fund selection platform: through AI technology, is committed to providing excell
class WenCai(ToolBase, ABC): class WenCai(ToolBase, ABC):
component_name = "WenCai" component_name = "WenCai"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 12)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("report", "") self.set_output("report", "")
@ -111,4 +111,4 @@ class WenCai(ToolBase, ABC):
assert False, self.output() assert False, self.output()
def thoughts(self) -> str: def thoughts(self) -> str:
return "Pulling live financial data for `{}`.".format(self.get_input().get("query", "-_-!")) return "Pulling live financial data for `{}`.".format(self.get_input().get("query", "-_-!"))

View File

@ -64,7 +64,7 @@ class WikipediaParam(ToolParamBase):
class Wikipedia(ToolBase, ABC): class Wikipedia(ToolBase, ABC):
component_name = "Wikipedia" component_name = "Wikipedia"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("query"): if not kwargs.get("query"):
self.set_output("formalized_content", "") self.set_output("formalized_content", "")
@ -99,6 +99,6 @@ class Wikipedia(ToolBase, ABC):
def thoughts(self) -> str: def thoughts(self) -> str:
return """ return """
Keywords: {} Keywords: {}
Looking for the most relevant articles. Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!")) """.format(self.get_input().get("query", "-_-!"))

View File

@ -72,7 +72,7 @@ class YahooFinanceParam(ToolParamBase):
class YahooFinance(ToolBase, ABC): class YahooFinance(ToolBase, ABC):
component_name = "YahooFinance" component_name = "YahooFinance"
@timeout(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)) @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)))
def _invoke(self, **kwargs): def _invoke(self, **kwargs):
if not kwargs.get("stock_code"): if not kwargs.get("stock_code"):
self.set_output("report", "") self.set_output("report", "")
@ -111,4 +111,4 @@ class YahooFinance(ToolBase, ABC):
assert False, self.output() assert False, self.output()
def thoughts(self) -> str: def thoughts(self) -> str:
return "Pulling live financial data for `{}`.".format(self.get_input().get("stock_code", "-_-!")) return "Pulling live financial data for `{}`.".format(self.get_input().get("stock_code", "-_-!"))

View File

@ -143,7 +143,7 @@ class Base(ABC):
logging.info("[HISTORY]" + json.dumps(history, ensure_ascii=False, indent=2)) logging.info("[HISTORY]" + json.dumps(history, ensure_ascii=False, indent=2))
if self.model_name.lower().find("qwen3") >= 0: if self.model_name.lower().find("qwen3") >= 0:
kwargs["extra_body"] = {"enable_thinking": False} kwargs["extra_body"] = {"enable_thinking": False}
response = self.client.chat.completions.create(model=self.model_name, messages=history, **gen_conf, **kwargs) 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): 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): def _chat_streamly(self, history, gen_conf, **kwargs):
logging.info("[HISTORY STREAMLY]" + json.dumps(history, ensure_ascii=False, indent=4)) logging.info("[HISTORY STREAMLY]" + json.dumps(history, ensure_ascii=False, indent=4))
reasoning_start = False reasoning_start = False
if kwargs.get("stop") or "stop" in gen_conf: 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")) response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf, stop=kwargs.get("stop"))
else: else:
response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf) response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf)
for resp in response: for resp in response:
if not resp.choices: if not resp.choices:
continue continue
@ -643,7 +643,7 @@ class ZhipuChat(Base):
del gen_conf["max_tokens"] del gen_conf["max_tokens"]
gen_conf = self._clean_conf_plealty(gen_conf) gen_conf = self._clean_conf_plealty(gen_conf)
return gen_conf return gen_conf
def _clean_conf_plealty(self, gen_conf): def _clean_conf_plealty(self, gen_conf):
if "presence_penalty" in gen_conf: if "presence_penalty" in gen_conf:
del gen_conf["presence_penalty"] del gen_conf["presence_penalty"]