diff options
| author | Michael Peter Christen <mc@yacy.net> | 2026-03-28 21:33:54 +0100 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2026-03-28 21:33:54 +0100 |
| commit | 1f80c6679e2fa240210cfa7a5df7441a1ba7c023 (patch) | |
| tree | 3fadb671c15688d4525f936e61f7104f0ed54de1 /source/net | |
| parent | e0bf6b8c68f0d141f1ee4c35a3e65108caf27615 (diff) | |
Add search tool backed by shared RAG search document output
Diffstat (limited to 'source/net')
| -rw-r--r-- | source/net/yacy/ai/RAGAugmentor.java | 37 | ||||
| -rw-r--r-- | source/net/yacy/ai/ToolProvider.java | 2 | ||||
| -rw-r--r-- | source/net/yacy/ai/tools/SearchTool.java | 77 | ||||
| -rw-r--r-- | source/net/yacy/http/servlets/RAGProxyServlet.java | 4 |
4 files changed, 117 insertions, 3 deletions
diff --git a/source/net/yacy/ai/RAGAugmentor.java b/source/net/yacy/ai/RAGAugmentor.java index bd8b979cc..71b285504 100644 --- a/source/net/yacy/ai/RAGAugmentor.java +++ b/source/net/yacy/ai/RAGAugmentor.java @@ -21,6 +21,8 @@ package net.yacy.ai; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -120,6 +122,41 @@ public final class RAGAugmentor { } /** + * Build the same markdown search document envelope used by chat RAG + * attachment injection and tool-based search retrieval. + * + * @param query query string + * @param count max number of search rows + * @param global when true, use global YaCy search + * @return JSON object containing filename, base64 markdown and plain content + * @throws JSONException when JSON assembly fails + */ + public static JSONObject searchResultsDocument(final String query, final int count, final boolean global) throws JSONException { + final String normalizedQuery = query == null ? "" : query.trim(); + final String effectiveQuery = normalizedQuery.isEmpty() ? "search" : normalizedQuery; + final String markdown = searchResultsAsMarkdown(effectiveQuery, count, global); + final JSONObject doc = new JSONObject(true); + doc.put("search-filename", "search_result_" + effectiveQuery.replace(' ', '_') + ".md"); + doc.put("search-text-base64", Base64.getEncoder().encodeToString(markdown.getBytes(StandardCharsets.UTF_8))); + doc.put("content", markdown); + return doc; + } + + /** + * Determine whether chat search should default to global retrieval on the + * current peer. + * + * @return true when peer-to-peer/global search is available + */ + public static boolean defaultSearchIsGlobal() { + final Switchboard sb = Switchboard.getSwitchboard(); + if (sb == null) return false; + final boolean clustersearch = sb.isRobinsonMode() + && SwitchboardConstants.CLUSTER_MODE_PUBLIC_CLUSTER.equals(sb.getConfig(SwitchboardConstants.CLUSTER_MODE, "")); + return sb.getConfigBool(SwitchboardConstants.INDEX_RECEIVE_ALLOW_SEARCH, true) || clustersearch; + } + + /** * Executes a global/distributed YaCy search event and maps results into a * compact JSON format. * diff --git a/source/net/yacy/ai/ToolProvider.java b/source/net/yacy/ai/ToolProvider.java index 997b01602..f03d1f327 100644 --- a/source/net/yacy/ai/ToolProvider.java +++ b/source/net/yacy/ai/ToolProvider.java @@ -39,6 +39,7 @@ import net.yacy.ai.tools.HttpJsonTool; import net.yacy.ai.tools.Mermaid2ASCIITool; import net.yacy.ai.tools.NumberParserTool; import net.yacy.ai.tools.PromptToMermaidTool; +import net.yacy.ai.tools.SearchTool; import net.yacy.ai.tools.SelfReflectTool; import net.yacy.ai.tools.TableOpsTool; import net.yacy.ai.tools.UnitConverterTool; @@ -81,6 +82,7 @@ public final class ToolProvider { new Mermaid2ASCIITool() ); private static final List<ToolHandler> TOOLS_RETRIEVAL = Arrays.asList( + new SearchTool(), new WikipediaLinkCreatorTool(), new WebFetchTool() ); diff --git a/source/net/yacy/ai/tools/SearchTool.java b/source/net/yacy/ai/tools/SearchTool.java new file mode 100644 index 000000000..d2d2a9f5b --- /dev/null +++ b/source/net/yacy/ai/tools/SearchTool.java @@ -0,0 +1,77 @@ +/** + * SearchTool + * Copyright 2026 by Michael Peter Christen + * First released 28.03.2026 at https://yacy.net + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +package net.yacy.ai.tools; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import net.yacy.ai.RAGAugmentor; +import net.yacy.ai.ToolHandler; + +public class SearchTool implements ToolHandler { + + private static final String NAME = "search"; + private static final int RESULT_COUNT = 10; + + @Override + public JSONObject definition() throws JSONException { + final JSONObject tool = new JSONObject(true); + tool.put("type", "function"); + final JSONObject fn = new JSONObject(true); + fn.put("name", NAME); + fn.put("description", "Search for documents in the YaCy index. This is like web search."); + + final JSONObject params = new JSONObject(true); + params.put("type", "object"); + final JSONObject props = new JSONObject(true); + + final JSONObject query = new JSONObject(true); + query.put("type", "string"); + query.put("description", "Search query to run against YaCy."); + props.put("query", query); + + params.put("properties", props); + params.put("required", new JSONArray().put("query")); + fn.put("parameters", params); + tool.put("function", fn); + return tool; + } + + @Override + public int maxCallsPerTurn() { + return 3; + } + + @Override + public String execute(final String arguments) { + final JSONObject args; + try { + args = (arguments == null || arguments.isEmpty()) ? new JSONObject(true) : new JSONObject(arguments); + } catch (final JSONException e) { + return ToolHandler.errorJson("Invalid arguments JSON"); + } + + final String query = args.optString("query", "").trim(); + if (query.isEmpty()) return ToolHandler.errorJson("Missing query"); + + try { + final JSONObject result = RAGAugmentor.searchResultsDocument(query, RESULT_COUNT, RAGAugmentor.defaultSearchIsGlobal()); + result.put("tool", NAME); + result.put("query", query); + result.put("global", RAGAugmentor.defaultSearchIsGlobal()); + return result.toString(); + } catch (final JSONException e) { + return ToolHandler.errorJson("Failed to build search result document"); + } + } +} diff --git a/source/net/yacy/http/servlets/RAGProxyServlet.java b/source/net/yacy/http/servlets/RAGProxyServlet.java index bc9d8aade..5875dc4fd 100644 --- a/source/net/yacy/http/servlets/RAGProxyServlet.java +++ b/source/net/yacy/http/servlets/RAGProxyServlet.java @@ -213,9 +213,7 @@ public class RAGProxyServlet extends HttpServlet { JSONObject initialMetadata = null; if (searchResultMarkdown.length() > 0) { - initialMetadata = new JSONObject(true); - initialMetadata.put("search-filename", "search_result_" + searchResultQuery.replace(' ', '_') + ".md"); - initialMetadata.put("search-text-base64", new String(Base64.getEncoder().encode(searchResultMarkdown.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)); + initialMetadata = RAGAugmentor.searchResultsDocument(searchResultQuery, 10, "global".equals(ragMode)); } // ToolCallProtocol owns request preparation, initial stream handling and follow-up tool rounds. |
