diff options
| author | Michael Peter Christen <mc@yacy.net> | 2026-07-04 17:08:36 +0200 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2026-07-04 17:08:36 +0200 |
| commit | e5d9efe6539f0f2ae46058da21bc7b810f7661b1 (patch) | |
| tree | 47413589b8b8af688b2997ca7a6e8db0f10a7d88 | |
| parent | 8f2f9c3ce4ef3f3ee56a1a5dc4810ad4dbad96b9 (diff) | |
fix for timeout
| -rw-r--r-- | source/net/yacy/ai/LLM.java | 31 | ||||
| -rw-r--r-- | source/net/yacy/ai/LogReportService.java | 10 |
2 files changed, 36 insertions, 5 deletions
diff --git a/source/net/yacy/ai/LLM.java b/source/net/yacy/ai/LLM.java index e22020f8d..5af3cd276 100644 --- a/source/net/yacy/ai/LLM.java +++ b/source/net/yacy/ai/LLM.java @@ -227,6 +227,11 @@ public class LLM { private static String sendPostRequest(final String urls, final JSONObject data, final String apiKey) throws IOException, URISyntaxException { final URL url = new URI(urls).toURL(); final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + // Batch calls like the log report generation run large models on CPU and may + // take many minutes for a single response. Nothing on our side is allowed to + // abort such a call: connecting must fail fast, but reading must never time out. + conn.setConnectTimeout(10000); + conn.setReadTimeout(0); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); if (apiKey != null && !apiKey.isEmpty()) { @@ -387,11 +392,35 @@ public class LLM { final JSONObject choice = choices.getJSONObject(0); final JSONObject message = choice.getJSONObject("message"); final String content = message.optString("content", ""); - return content; + // A truncated answer is not an abort on our side but a generation limit; + // make the cause visible because the caller only sees a fragment. + final String finishReason = choice.optString("finish_reason", ""); + if ("length".equals(finishReason)) { + log.warn("chat response was truncated by the max_tokens limit (" + max_tokens + + "), model=" + LogRedaction.redact(model) + + ", contentChars=" + content.length() + + ". Configure a higher max_tokens for this model if complete outputs are required."); + } + return stripThinkBlocks(content); } catch (JSONException | URISyntaxException e) { throw new IOException(e.getMessage()); } } + + /** + * Remove reasoning blocks from a chat answer. Some backends deliver the chain of + * thought of thinking models inline as <think>...</think> in the content; + * a report or answer must only contain the text after the reasoning. An unclosed + * think block (e.g. because the response was cut by the token limit) is removed + * up to the end of the content. + */ + protected static String stripThinkBlocks(final String content) { + if (content == null || content.indexOf("<think>") < 0) return content; + final String withoutClosed = content.replaceAll("(?s)<think>.*?</think>", ""); + final int unclosed = withoutClosed.indexOf("<think>"); + final String result = unclosed < 0 ? withoutClosed : withoutClosed.substring(0, unclosed); + return result.trim(); + } public String chat(final String model, final String systemPrompt, final String userPrompt, final int max_tokens) throws IOException { try { diff --git a/source/net/yacy/ai/LogReportService.java b/source/net/yacy/ai/LogReportService.java index 23662984e..3e0792223 100644 --- a/source/net/yacy/ai/LogReportService.java +++ b/source/net/yacy/ai/LogReportService.java @@ -236,10 +236,12 @@ public class LogReportService { log.info("Log report scheduler is disabled by " + CONFIG_ENABLED + "."); return null; } - if (!hasConfiguredLogReportModel()) { - log.info("Log report scheduler is inactive because no production model is configured for logreport usage."); - return null; - } + // Note: the scheduler is started even when no logreport model is configured + // yet. The model assignment can happen at any time on /LLMSelection_p.html + // and must not require a restart; the tick below skips (with a single log + // line) as long as the model is missing and picks up work automatically once + // it is configured. Do not add an early return here for a missing model - + // that would silently disable report generation until the next restart. final LogReportService service = new LogReportService(sb); final long initialDelay = Math.max(0L, sb.getConfigLong(CONFIG_INITIAL_DELAY_MINUTES, DEFAULT_INITIAL_DELAY_MINUTES)); |
