From 9868f80e9c275b1d6440b45fe8589736f6470227 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 16 Jun 2026 10:19:44 +0200 Subject: [PATCH 1/4] feat(tools): Extend context chat tool with scope_type and scope_list params Assisted-by: ClaudeCode:claude-opus-4-8 Signed-off-by: Marcel Klehr --- ex_app/lib/all_tools/context_chat.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/ex_app/lib/all_tools/context_chat.py b/ex_app/lib/all_tools/context_chat.py index a8e5105..990d488 100644 --- a/ex_app/lib/all_tools/context_chat.py +++ b/ex_app/lib/all_tools/context_chat.py @@ -1,5 +1,6 @@ # SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later +from typing import Literal from langchain_core.tools import tool from nc_py_api import AsyncNextcloudApp @@ -11,17 +12,32 @@ async def get_tools(nc: AsyncNextcloudApp): @tool @safe_tool - async def ask_context_chat(question: str) -> str: + async def ask_context_chat( + question: str, + scope_type: Literal['none', 'source', 'provider'] = 'none', + scope_list: list[str] | None = None, + ) -> str: """ Ask the context chat oracle a question about the user's documents. It knows the contents of all of the users documents. + This is often easier than searching for documents and then fetching their contents when trying to answer questions. :param question: The question to ask + :param scope_type: Optional. Restricts which documents the oracle searches. + - "none" (default): search across all of the user's documents. + - "source": restrict the search to specific items listed in scope_list. + - "provider": restrict the search to specific content providers listed in scope_list. + :param scope_list: Required when scope_type is "source" or "provider"; ignored otherwise. + - For scope_type "source": a list of source IDs in the format "__: " + (e.g., "files__default: 12345" for the file with Nextcloud file id 12345). File ids can be + obtained from the file-related tools or unified search. + - For scope_type "provider": a list of provider keys in the format "__" + (e.g., "files__default" for files, "mail__messages" for mail). :return: the answer from context chat """ task_input = { 'prompt': question, - 'scopeType': 'none', - 'scopeList': [], + 'scopeType': scope_type, + 'scopeList': scope_list or [], 'scopeListMeta': '', } task_output = (await run_task(nc, "context_chat:context_chat", task_input)).output From c94d33606c6f150fe5c7335d60efc1127e875f67 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 24 Jun 2026 10:17:25 +0200 Subject: [PATCH 2/4] fix(context_chat): Prompt engineering Co-authored-by: Anupam Kumar Signed-off-by: Marcel Klehr --- ex_app/lib/all_tools/context_chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ex_app/lib/all_tools/context_chat.py b/ex_app/lib/all_tools/context_chat.py index 990d488..8aec086 100644 --- a/ex_app/lib/all_tools/context_chat.py +++ b/ex_app/lib/all_tools/context_chat.py @@ -22,7 +22,7 @@ async def ask_context_chat( This is often easier than searching for documents and then fetching their contents when trying to answer questions. :param question: The question to ask :param scope_type: Optional. Restricts which documents the oracle searches. - - "none" (default): search across all of the user's documents. + - "none" (default): search across all of the user's documents and content providers. - "source": restrict the search to specific items listed in scope_list. - "provider": restrict the search to specific content providers listed in scope_list. :param scope_list: Required when scope_type is "source" or "provider"; ignored otherwise. From bb8bf63e984ba9a3e16bc479628c258bb3e68d41 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 24 Jun 2026 10:23:41 +0200 Subject: [PATCH 3/4] feat: Allow listing context_chat providers Signed-off-by: Marcel Klehr --- ex_app/lib/all_tools/context_chat.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ex_app/lib/all_tools/context_chat.py b/ex_app/lib/all_tools/context_chat.py index 8aec086..21dd882 100644 --- a/ex_app/lib/all_tools/context_chat.py +++ b/ex_app/lib/all_tools/context_chat.py @@ -1,5 +1,6 @@ # SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors # SPDX-License-Identifier: AGPL-3.0-or-later +import json from typing import Literal from langchain_core.tools import tool from nc_py_api import AsyncNextcloudApp @@ -10,6 +11,20 @@ async def get_tools(nc: AsyncNextcloudApp): + @tool + @safe_tool + async def list_context_chat_providers() -> str: + """ + List the content providers available to context chat (e.g., files, mail). + Use this to discover valid provider keys for ask_context_chat's scope_list when scope_type is "provider". + :return: JSON array of available providers + """ + response = await nc._session._create_adapter(False).request('GET', f"{nc.app_cfg.endpoint}/index.php/apps/context_chat/providers", headers={ + "Content-Type": "application/json", + "OCS-APIREQUEST": "true", + }) + return json.dumps(response.json()) + @tool @safe_tool async def ask_context_chat( @@ -45,6 +60,7 @@ async def ask_context_chat( return [ ask_context_chat, + list_context_chat_providers, ] def get_category_name(): From 1310bd0213190d426d3a54b31e97c5a9701e9da0 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 24 Jun 2026 10:50:31 +0200 Subject: [PATCH 4/4] fix(context_chat): Prompt engineering Signed-off-by: Marcel Klehr --- ex_app/lib/all_tools/context_chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ex_app/lib/all_tools/context_chat.py b/ex_app/lib/all_tools/context_chat.py index 21dd882..ad87944 100644 --- a/ex_app/lib/all_tools/context_chat.py +++ b/ex_app/lib/all_tools/context_chat.py @@ -16,7 +16,7 @@ async def get_tools(nc: AsyncNextcloudApp): async def list_context_chat_providers() -> str: """ List the content providers available to context chat (e.g., files, mail). - Use this to discover valid provider keys for ask_context_chat's scope_list when scope_type is "provider". + Use this to discover valid provider keys for ask_context_chat's scope_list when scope_type is "provider". Note that the files__default provider is always available. :return: JSON array of available providers """ response = await nc._session._create_adapter(False).request('GET', f"{nc.app_cfg.endpoint}/index.php/apps/context_chat/providers", headers={