Skip to content

Commit fa4f978

Browse files
authored
Merge pull request #181 from nextcloud/feat/web-fetch
feat(tools): Add web_fetch tool
2 parents 630df5e + cc9cb39 commit fa4f978

6 files changed

Lines changed: 40 additions & 7 deletions

File tree

ex_app/lib/agent.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ async def call_model(
127127
At the end of each message to the user, if you have carried out a task or answered a question, suggest up to three actions for things you can do for the user based on the tools you have available and details of the previous task. For example: If the user wants to know the weather for some location, they might be planning an event, you can suggest to create an event for them, or if they searched for a file, they may want to share it with others, suggest to create a share link for them, if they want a summary of something, you can suggest them to send the summary to somebody.
128128
"""
129129
if tool_enabled("duckduckgo_results_json"):
130-
system_prompt_text += "Only use the duckduckgo_results_json tool if the user explicitly asks for a web search.\n"
130+
system_prompt_text += "Use the duckduckgo_results_json tool if the user explicitly asks for a web search or you don't know about a topic or concept that the user is referencing.\n"
131131
if tool_enabled("list_talk_conversations"):
132132
system_prompt_text += "Use the list_talk_conversations tool to check which conversations exist.\n"
133133
if tool_enabled("list_calendars"):
@@ -140,8 +140,10 @@ async def call_model(
140140
system_prompt_text += "Use the find_details_of_current_user tool to find the current user's location and timezone.\n"
141141
if tool_enabled("list_mails"):
142142
system_prompt_text += "Always check for the mail account id before requesting a folder list.\n"
143+
if tool_enabled("web_fetch"):
144+
system_prompt_text += "Use the web_fetch tool to fetch web content. You can fetch the complete page content of a duckduckgo search result using web_fetch as well.\n"
143145

144-
if task['input'].get('memories', None) is not None:
146+
if task['input'].get('memories', None) is not None and task['input'].get('memories', None) is not []:
145147
system_prompt_text += "You can remember things from other conversations with the user. If relevant, take into account the following memories:\n\n" + "\n".join(task['input']['memories']) + "\n\n"
146148
# this is similar to customizing the create_react_agent with state_modifier, but is a lot more flexible
147149
system_prompt = SystemMessage(

ex_app/lib/all_tools/calendar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def find_free_time_slot_in_calendar_sync(participants: list[str], slot_duration:
165165
headers={
166166
"Content-Type": "text/calendar; charset=utf-8",
167167
"Depth": "0",
168-
}, content=freebusyRequest)
168+
}, data=freebusyRequest)
169169
print(freebusyRequest)
170170
print(response.text)
171171

ex_app/lib/all_tools/contacts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def find_person_in_contacts(name: str) -> list[dict[str, typing.Any]]:
7070
response = await nc._session._create_adapter(True).request('REPORT', f"{nc.app_cfg.endpoint}{link}", headers={
7171
"Content-Type": "application/xml; charset=utf-8",
7272
"Depth": "1",
73-
}, content=xml_body)
73+
}, data=xml_body)
7474

7575
if response.status_code != 207: # Multi-Status
7676
raise Exception(f"Error: {response.status_code} - {response.reason_phrase}")

ex_app/lib/all_tools/files.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async def get_tools(nc: AsyncNextcloudApp):
1515
@safe_tool
1616
async def get_file_content(file_path: str):
1717
"""
18-
Get the content of a file
18+
Get the content of a nextcloud-internal file of the current user
1919
:param file_path: the path of the file
2020
:return:
2121
"""
@@ -33,7 +33,7 @@ async def get_file_content(file_path: str):
3333
async def get_file_content_by_file_link(file_url: str):
3434
"""
3535
Get the content of a file given an internal Nextcloud link (e.g., https://host/index.php/f/12345)
36-
:param file_url: the internal file URL
36+
:param file_url: the nextcloud-internal file URL
3737
:return: text content of the file
3838
"""
3939

ex_app/lib/all_tools/web.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
import niquests
4+
from langchain_core.tools import tool
5+
from nc_py_api import AsyncNextcloudApp
6+
7+
from ex_app.lib.all_tools.lib.decorator import safe_tool
8+
9+
10+
async def get_tools(nc: AsyncNextcloudApp):
11+
12+
@tool
13+
@safe_tool
14+
async def web_fetch(url: str) -> str:
15+
"""
16+
Get the contents of a web page via HTTP
17+
:param url: The HTTP URL to the web page (e.g. https://nextcloud.com/team/ )
18+
:return: the web page content
19+
"""
20+
res = await niquests.get(url)
21+
return res.text()
22+
23+
return [
24+
web_fetch,
25+
]
26+
27+
def get_category_name():
28+
return "Web access"
29+
30+
async def is_available(nc: AsyncNextcloudApp):
31+
return True

ex_app/lib/nc_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class ChatWithNextcloud(BaseChatModel):
3737
tools: Sequence[
3838
Union[typing.Dict[str, Any], type, Callable, BaseTool]] = []
3939
TIMEOUT: int = 60 * 30 # 30 minutes
40-
MAX_MESSAGE_HISTORY: int = 13
40+
MAX_MESSAGE_HISTORY: int = 42
4141

4242
def _generate(self, messages: list[BaseMessage], stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any):
4343
raise Exception("Use _agenerate instead")

0 commit comments

Comments
 (0)