zrb 1.16.5__py3-none-any.whl → 1.17.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
zrb/builtin/__init__.py CHANGED
@@ -16,6 +16,7 @@ from zrb.builtin.project.add.fastapp.fastapp_task import add_fastapp_to_project
16
16
  from zrb.builtin.project.create.project_task import create_project
17
17
  from zrb.builtin.python import format_python_code
18
18
  from zrb.builtin.random import shuffle_values, throw_dice
19
+ from zrb.builtin.searxng.start import start_searxng
19
20
  from zrb.builtin.setup.asdf.asdf import setup_asdf
20
21
  from zrb.builtin.setup.latex.ubuntu import setup_latex_on_ubuntu
21
22
  from zrb.builtin.setup.tmux.tmux import setup_tmux
zrb/builtin/group.py CHANGED
@@ -41,7 +41,7 @@ todo_group = _maybe_add_group(Group(name="todo", description="✅ Todo managemen
41
41
  shell_group = _maybe_add_group(
42
42
  Group(name="shell", description="💬 Shell related commands")
43
43
  )
44
- shell_autocomplete_group: Group = shell_group.add_group(
44
+ shell_autocomplete_group = shell_group.add_group(
45
45
  Group(name="autocomplete", description="⌨️ Shell autocomplete related commands")
46
46
  )
47
47
 
@@ -59,3 +59,7 @@ setup_group = _maybe_add_group(Group(name="setup", description="🔧 Setup"))
59
59
  setup_latex_group = setup_group.add_group(
60
60
  Group(name="latex", description="✍️ Setup LaTeX")
61
61
  )
62
+
63
+ searxng_group = _maybe_add_group(
64
+ Group(name="searxng", description="🔎 Searxng related command")
65
+ )
@@ -6,7 +6,10 @@ from zrb.builtin.group import llm_group
6
6
  from zrb.builtin.llm.chat_session import get_llm_ask_input_mapping, read_user_prompt
7
7
  from zrb.builtin.llm.history import read_chat_conversation, write_chat_conversation
8
8
  from zrb.builtin.llm.input import PreviousSessionInput
9
- from zrb.builtin.llm.tool.api import get_current_location, get_current_weather
9
+ from zrb.builtin.llm.tool.api import (
10
+ create_get_current_location,
11
+ create_get_current_weather,
12
+ )
10
13
  from zrb.builtin.llm.tool.cli import run_shell_command
11
14
  from zrb.builtin.llm.tool.code import analyze_repo
12
15
  from zrb.builtin.llm.tool.file import (
@@ -28,8 +31,6 @@ from zrb.builtin.llm.tool.note import (
28
31
  from zrb.builtin.llm.tool.web import (
29
32
  create_search_internet_tool,
30
33
  open_web_page,
31
- search_arxiv,
32
- search_wikipedia,
33
34
  )
34
35
  from zrb.callback.callback import Callback
35
36
  from zrb.config.config import CFG
@@ -85,16 +86,12 @@ def _get_tool(ctx: AnyContext) -> list["ToolOrCallable"]:
85
86
  tools.append(run_shell_command)
86
87
  if CFG.LLM_ALLOW_OPEN_WEB_PAGE:
87
88
  tools.append(open_web_page)
88
- if CFG.LLM_ALLOW_SEARCH_WIKIPEDIA:
89
- tools.append(search_wikipedia)
90
- if CFG.LLM_ALLOW_SEARCH_ARXIV:
91
- tools.append(search_arxiv)
92
89
  if CFG.LLM_ALLOW_GET_CURRENT_LOCATION:
93
- tools.append(get_current_location)
90
+ tools.append(create_get_current_location())
94
91
  if CFG.LLM_ALLOW_GET_CURRENT_WEATHER:
95
- tools.append(get_current_weather)
92
+ tools.append(create_get_current_weather())
96
93
  if CFG.SERPAPI_KEY != "" and CFG.LLM_ALLOW_SEARCH_INTERNET:
97
- tools.append(create_search_internet_tool(CFG.SERPAPI_KEY))
94
+ tools.append(create_search_internet_tool())
98
95
  return tools
99
96
 
100
97
 
@@ -1,71 +1,85 @@
1
- from typing import Any, Literal
2
-
3
-
4
- def get_current_location() -> dict[str, float]:
5
- """
6
- Fetches the user's current geographical location based on their IP address.
7
-
8
- Use this tool when the user asks "Where am I?", "What is my current
9
- location?", or has a query that requires knowing their location to be
10
- answered.
11
-
12
- Returns:
13
- dict[str, float]: A dictionary containing the 'lat' and 'lon' of the current
14
- location.
15
- Example: {"lat": 48.8584, "lon": 2.2945}
16
- Raises:
17
- requests.RequestException: If the API request to the location service
18
- fails.
19
- """
20
- import requests
21
-
22
- try:
23
- response = requests.get("http://ip-api.com/json?fields=lat,lon", timeout=5)
24
- response.raise_for_status()
25
- return dict(response.json())
26
- except requests.RequestException as e:
27
- raise requests.RequestException(f"Failed to get location: {e}") from None
28
-
29
-
30
- def get_current_weather(
31
- latitude: float,
32
- longitude: float,
33
- temperature_unit: Literal["celsius", "fahrenheit"],
34
- ) -> dict[str, Any]:
35
- """
36
- Retrieves the current weather conditions for a given geographical location.
37
-
38
- Use this tool when the user asks about the weather. If the user does not
39
- provide a location, first use the `get_current_location` tool to
40
- determine their location.
41
-
42
- Args:
43
- latitude (float): The latitude of the location.
44
- longitude (float): The longitude of the location.
45
- temperature_unit (Literal["celsius", "fahrenheit"]): The desired unit
46
- for the temperature reading.
47
-
48
- Returns:
49
- dict[str, Any]: A dictionary containing detailed weather data, including
50
- temperature, wind speed, and weather code.
51
- Raises:
52
- requests.RequestException: If the API request to the weather service
53
- fails.
54
- """
55
- import requests
56
-
57
- try:
58
- response = requests.get(
59
- "https://api.open-meteo.com/v1/forecast",
60
- params={
61
- "latitude": latitude,
62
- "longitude": longitude,
63
- "temperature_unit": temperature_unit,
64
- "current_weather": True,
65
- },
66
- timeout=5,
67
- )
68
- response.raise_for_status()
69
- return dict(response.json())
70
- except requests.RequestException as e:
71
- raise requests.RequestException(f"Failed to get weather data: {e}") from None
1
+ from typing import Any, Callable, Literal
2
+
3
+ from zrb.config.llm_config import llm_config
4
+
5
+
6
+ def create_get_current_location() -> Callable:
7
+ if llm_config.default_current_location_tool is not None:
8
+ return llm_config.default_current_location_tool
9
+
10
+ def get_current_location() -> dict[str, float]:
11
+ """
12
+ Fetches the user's current geographical location based on their IP address.
13
+
14
+ Use this tool when the user asks "Where am I?", "What is my current
15
+ location?", or has a query that requires knowing their location to be
16
+ answered.
17
+
18
+ Returns:
19
+ dict[str, float]: A dictionary containing the 'lat' and 'lon' of the current
20
+ location.
21
+ Example: {"lat": 48.8584, "lon": 2.2945}
22
+ Raises:
23
+ requests.RequestException: If the API request to the location service
24
+ fails.
25
+ """
26
+ import requests
27
+
28
+ try:
29
+ response = requests.get("http://ip-api.com/json?fields=lat,lon", timeout=5)
30
+ response.raise_for_status()
31
+ return dict(response.json())
32
+ except requests.RequestException as e:
33
+ raise requests.RequestException(f"Failed to get location: {e}")
34
+
35
+ return get_current_location
36
+
37
+
38
+ def create_get_current_weather() -> Callable:
39
+ if llm_config.default_current_weather_tool is not None:
40
+ return llm_config.default_current_weather_tool
41
+
42
+ def get_current_weather(
43
+ latitude: float,
44
+ longitude: float,
45
+ temperature_unit: Literal["celsius", "fahrenheit"],
46
+ ) -> dict[str, Any]:
47
+ """
48
+ Retrieves the current weather conditions for a given geographical location.
49
+
50
+ Use this tool when the user asks about the weather. If the user does not
51
+ provide a location, first use the `get_current_location` tool to
52
+ determine their location.
53
+
54
+ Args:
55
+ latitude (float): The latitude of the location.
56
+ longitude (float): The longitude of the location.
57
+ temperature_unit (Literal["celsius", "fahrenheit"]): The desired unit
58
+ for the temperature reading.
59
+
60
+ Returns:
61
+ dict[str, Any]: A dictionary containing detailed weather data, including
62
+ temperature, wind speed, and weather code.
63
+ Raises:
64
+ requests.RequestException: If the API request to the weather service
65
+ fails.
66
+ """
67
+ import requests
68
+
69
+ try:
70
+ response = requests.get(
71
+ "https://api.open-meteo.com/v1/forecast",
72
+ params={
73
+ "latitude": latitude,
74
+ "longitude": longitude,
75
+ "temperature_unit": temperature_unit,
76
+ "current_weather": True,
77
+ },
78
+ timeout=5,
79
+ )
80
+ response.raise_for_status()
81
+ return dict(response.json())
82
+ except requests.RequestException as e:
83
+ raise requests.RequestException(f"Failed to get weather data: {e}")
84
+
85
+ return get_current_weather
@@ -20,6 +20,7 @@ def write_long_term_note(content: str) -> str:
20
20
  Writes content to the global long-term note.
21
21
  The content of this note is intended to be read by the AI Agent in the future.
22
22
  It should contain global information/preference.
23
+ Use this tool proactively when you find information worth noted.
23
24
 
24
25
  Args:
25
26
  content: The content to write to the long-term note (overwriting the existing one).
@@ -58,6 +59,8 @@ def write_contextual_note(content: str, path: str | None = None) -> str:
58
59
 
59
60
  If no path is provided, it defaults to the current working directory.
60
61
 
62
+ Use this tool proactively when you find information worth noted.
63
+
61
64
  Args:
62
65
  content: The new content of the note (overwriting the existing one).
63
66
  path: The file or directory path to associate the note with.
@@ -2,6 +2,9 @@ from collections.abc import Callable
2
2
  from typing import Any
3
3
  from urllib.parse import urljoin
4
4
 
5
+ from zrb.config.config import CFG
6
+ from zrb.config.llm_config import llm_config
7
+
5
8
  _DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
6
9
 
7
10
 
@@ -28,26 +31,11 @@ async def open_web_page(url: str) -> dict[str, Any]:
28
31
  return {"content": markdown_content, "links_on_page": links}
29
32
 
30
33
 
31
- def create_search_internet_tool(
32
- serp_api_key: str,
33
- ) -> Callable[[str, int], dict[str, Any]]:
34
- """
35
- Creates a tool that searches the internet using the SerpAPI Google Search
36
- API.
37
-
38
- This factory returns a function that can be used to find information on the
39
- web. The generated tool is the primary way to answer general knowledge
40
- questions or to find information on topics you are unfamiliar with.
41
-
42
- Args:
43
- serp_api_key (str): The API key for SerpAPI.
44
-
45
- Returns:
46
- Callable: A function that takes a search query and returns a list of
47
- search results.
48
- """
34
+ def create_search_internet_tool() -> Callable:
35
+ if llm_config.default_search_internet_tool is not None:
36
+ return llm_config.default_search_internet_tool
49
37
 
50
- def search_internet(query: str, num_results: int = 10) -> dict[str, Any]:
38
+ def search_internet(query: str, page: int = 1) -> dict[str, Any]:
51
39
  """
52
40
  Performs an internet search using Google and returns a summary of the results.
53
41
 
@@ -56,7 +44,7 @@ def create_search_internet_tool(
56
44
 
57
45
  Args:
58
46
  query (str): The search query.
59
- num_results (int, optional): The desired number of search results. Defaults to 10.
47
+ Page (int, optional): The search page number, default to 1.
60
48
 
61
49
  Returns:
62
50
  dict[str, Any]: A formatted string summarizing the search results,
@@ -64,17 +52,29 @@ def create_search_internet_tool(
64
52
  """
65
53
  import requests
66
54
 
67
- response = requests.get(
68
- "https://serpapi.com/search",
69
- headers={"User-Agent": _DEFAULT_USER_AGENT},
70
- params={
71
- "q": query,
72
- "num": num_results,
73
- "hl": "en",
74
- "safe": "off",
75
- "api_key": serp_api_key,
76
- },
77
- )
55
+ if CFG.SEARCH_INTERNET_METHOD.strip() == "serpapi" and CFG.SERPAPI_KEY != "":
56
+ response = requests.get(
57
+ "https://serpapi.com/search",
58
+ headers={"User-Agent": _DEFAULT_USER_AGENT},
59
+ params={
60
+ "q": query,
61
+ "start": (page - 1) * 10,
62
+ "hl": "en",
63
+ "safe": "off",
64
+ "api_key": CFG.SERPAPI_KEY,
65
+ },
66
+ )
67
+ else:
68
+ response = requests.get(
69
+ url=f"{CFG.SEARXNG_BASE_URL}/search",
70
+ headers={"User-Agent": _DEFAULT_USER_AGENT},
71
+ params={
72
+ "q": query,
73
+ "format": "json",
74
+ "pageno": page,
75
+ "safesearch": 0,
76
+ },
77
+ )
78
78
  if response.status_code != 200:
79
79
  raise Exception(
80
80
  f"Error: Unable to retrieve search results (status code: {response.status_code})" # noqa
@@ -84,62 +84,6 @@ def create_search_internet_tool(
84
84
  return search_internet
85
85
 
86
86
 
87
- def search_wikipedia(query: str) -> dict[str, Any]:
88
- """
89
- Searches for articles on Wikipedia.
90
-
91
- This is a specialized search tool for querying Wikipedia. It's best for
92
- when the user is asking for definitions, historical information, or
93
- biographical details that are likely to be found on an encyclopedia.
94
-
95
- Args:
96
- query (str): The search term or question.
97
-
98
- Returns:
99
- dict[str, Any]: The raw JSON response from the Wikipedia API, containing a list of
100
- search results.
101
- """
102
- import requests
103
-
104
- params = {"action": "query", "list": "search", "srsearch": query, "format": "json"}
105
- response = requests.get(
106
- "https://en.wikipedia.org/w/api.php",
107
- headers={"User-Agent": _DEFAULT_USER_AGENT},
108
- params=params,
109
- )
110
- return response.json()
111
-
112
-
113
- def search_arxiv(query: str, num_results: int = 10) -> dict[str, Any]:
114
- """
115
- Searches for academic papers and preprints on ArXiv.
116
-
117
- Use this tool when the user's query is scientific or technical in nature
118
- and they are likely looking for research papers, articles, or academic
119
- publications.
120
-
121
- Args:
122
- query (str): The search query, which can include keywords, author
123
- names, or titles.
124
- num_results (int, optional): The maximum number of results to return.
125
- Defaults to 10.
126
-
127
- Returns:
128
- dict[str, Any]: The raw XML response from the ArXiv API, containing a list of
129
- matching papers.
130
- """
131
- import requests
132
- import xmltodict
133
-
134
- params = {"search_query": f"all:{query}", "start": 0, "max_results": num_results}
135
- response = requests.get(
136
- "http://export.arxiv.org/api/query",
137
- headers={"User-Agent": _DEFAULT_USER_AGENT},
138
- params=params,
139
- )
140
- return xmltodict.parse(response.content)
141
-
142
-
143
87
  async def _fetch_page_content(url: str) -> tuple[str, list[str]]:
144
88
  """Fetches the HTML content and all absolute links from a URL."""
145
89
  try: