From: Moritz Friedrich Date: Tue, 28 Jul 2026 07:47:57 +0000 (+0200) Subject: Refactor: Move request content generation into gpt_check function X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F6159%2Fhead;p=thirdparty%2Frspamd.git Refactor: Move request content generation into gpt_check function The Condition option was previously expected to return both a boolean decision and the content for the LLM request. This behavior was non-obvious and only discovered through extensive debugging and code analysis (confirmed by rspamd#5857). The condition setting now only returns a boolean for the decision (true/false) and an optional reason. Generating the request content has been moved into the gpt_check function, making the condition callback simpler to implement. Example condition: ``` condition = < 8.0 then return false, ‘outside the range’ end end return true, nil end EOD ``` --- diff --git a/src/plugins/lua/gpt.lua b/src/plugins/lua/gpt.lua index 1b4b21d06b..3a2932c469 100644 --- a/src/plugins/lua/gpt.lua +++ b/src/plugins/lua/gpt.lua @@ -403,24 +403,7 @@ local function default_condition(task) end end - -- Get displayed text part with configurable min_words - local sel_part = lua_mime.get_displayed_text_part(task, settings.min_words) - if not sel_part then - return false, 'no text part found' - end - - -- Unified LLM input building (subject/from/urls/body one-line) - local model_cfg = settings.model_parameters[settings.model] or {} - local max_tokens = model_cfg.max_completion_tokens or model_cfg.max_tokens or 1000 - local input_tbl = llm_common.build_llm_input(task, { - max_tokens = max_tokens, - reply_trim_mode = settings.reply_trim_mode, - min_words = settings.min_words, - }) - if not input_tbl then - return false, 'no content to send' - end - return true, input_tbl, sel_part + return true, nil end local function maybe_extract_json(str) @@ -1211,17 +1194,16 @@ local function ollama_check(task, content, sel_part, context_snippet) end local function gpt_check(task) - local ret, content, sel_part = settings.condition(task) + local ret, ret_msg = settings.condition(task) + + -- Get displayed text part with configurable min_words + local sel_part = lua_mime.get_displayed_text_part(task, settings.min_words) -- Always update context if enabled, even when condition is not met local context_enabled = redis_params and settings.context and is_context_enabled_for_task(task) if context_enabled and not ret then -- Condition not met (e.g. BAYES_SPAM, passthrough, etc.) -- Update context without LLM call; infer result from task metrics - if not sel_part then - -- Try to get text part for context update - sel_part = lua_mime.get_displayed_text_part(task) - end if sel_part then local result = task:get_metric_result() local inferred_result = nil @@ -1234,15 +1216,24 @@ local function gpt_check(task) end llm_context.update_after_classification(task, redis_params, settings.context, inferred_result, sel_part, N) end - lua_util.debugm(N, task, "skip checking gpt as the condition is not met: %s; context updated", content) + lua_util.debugm(N, task, "skip checking gpt as the condition is not met: %s; context updated", ret_msg) return end if not ret then - lua_util.debugm(N, task, "skip checking gpt as the condition is not met: %s", content) + lua_util.debugm(N, task, "skip checking gpt as the condition is not met: %s", ret_msg) return end + -- Unified LLM input building (subject/from/urls/body one-line) + local model_cfg = settings.model_parameters[settings.model] or {} + local max_tokens = model_cfg.max_completion_tokens or model_cfg.max_tokens or 1000 + local content = llm_common.build_llm_input(task, { + max_tokens = max_tokens, + reply_trim_mode = settings.reply_trim_mode, + min_words = settings.min_words, + }) + if not content then lua_util.debugm(N, task, "no content to send to gpt classification") return