Skip to content

Commit 5dc7d55

Browse files
committed
Formatting
1 parent 6337a31 commit 5dc7d55

File tree

4 files changed

+151
-50
lines changed

4 files changed

+151
-50
lines changed

llm_tools_searxng.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,73 @@
1-
import llm
2-
import httpx
31
import json
42
import os
53

4+
import httpx
5+
import llm
6+
67

78
class SearXNG:
89
"""A tool for searching the web using SearXNG search engine"""
9-
10+
1011
def __init__(self, base_url: str = None):
1112
self.base_url = base_url or os.getenv("SEARXNG_URL")
1213
if not self.base_url:
1314
raise ValueError("SEARXNG_URL environment variable is required")
1415
self.method = os.getenv("SEARXNG_METHOD", "POST").upper()
15-
16-
def search(self, query: str, format: str = "json", categories: str = None,
17-
engines: str = None, language: str = "en", pageno: int = 1,
18-
time_range: str = None, safesearch: int = 1) -> str:
16+
17+
def search(
18+
self,
19+
query: str,
20+
format: str = "json",
21+
categories: str = None,
22+
engines: str = None,
23+
language: str = "en",
24+
pageno: int = 1,
25+
time_range: str = None,
26+
safesearch: int = 1,
27+
) -> str:
1928
"""
2029
Search the web using SearXNG.
21-
30+
2231
Args:
2332
query: The search query (required)
2433
format: Output format (json, csv, rss) - default: json
2534
categories: Comma separated list of search categories (optional)
26-
engines: Comma separated list of search engines (optional)
35+
engines: Comma separated list of search engines (optional)
2736
language: Language code - default: en
2837
pageno: Search page number - default: 1
2938
time_range: Time range (day, month, year) - optional
3039
safesearch: Safe search level (0, 1, 2) - default: 1
3140
"""
3241
search_url = f"{self.base_url.rstrip('/')}/search"
33-
42+
3443
params = {
3544
"q": query,
3645
"format": format,
3746
"language": language,
3847
"pageno": pageno,
39-
"safesearch": safesearch
48+
"safesearch": safesearch,
4049
}
41-
50+
4251
if categories:
4352
params["categories"] = categories
4453
if engines:
4554
params["engines"] = engines
4655
if time_range:
4756
params["time_range"] = time_range
48-
57+
4958
try:
5059
# TODO: Set user agent?
5160
headers = None
52-
53-
with httpx.Client(follow_redirects=True, timeout=30.0, headers=headers) as client:
61+
62+
with httpx.Client(
63+
follow_redirects=True, timeout=30.0, headers=headers
64+
) as client:
5465
if self.method == "POST":
5566
response = client.post(search_url, data=params)
5667
else:
5768
response = client.get(search_url, params=params)
5869
response.raise_for_status()
59-
70+
6071
if format == "json":
6172
result = response.json()
6273
if "results" in result:
@@ -66,21 +77,21 @@ def search(self, query: str, format: str = "json", categories: str = None,
6677
"title": item.get("title", ""),
6778
"url": item.get("url", ""),
6879
"snippet": item.get("content", ""),
69-
"engine": item.get("engine", "")
80+
"engine": item.get("engine", ""),
7081
}
7182
formatted_results.append(formatted_result)
72-
83+
7384
summary = {
7485
"query": query,
7586
"number_of_results": len(result.get("results", [])),
76-
"results": formatted_results
87+
"results": formatted_results,
7788
}
7889
return json.dumps(summary, indent=2)
7990
else:
8091
return json.dumps(result, indent=2)
8192
else:
8293
return response.text
83-
94+
8495
except httpx.HTTPError as e:
8596
raise Exception(f"HTTP error occurred: {e}")
8697
except json.JSONDecodeError as e:
@@ -93,7 +104,7 @@ def searxng_search(query: str) -> str:
93104
"""
94105
Search the web using SearXNG. Returns a JSON string with results including
95106
title, URL, snippet, and search engine used.
96-
107+
97108
Args:
98109
query: Search query to search for. SearxNG search syntax (https://docs.searxng.org/user/search-syntax.html) is supported.
99110
"""

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ dependencies = [
1616
requires = ["setuptools"]
1717
build-backend = "setuptools.build_meta"
1818

19+
[dependency-groups]
20+
dev = [
21+
"black>=25.1.0",
22+
"isort>=6.0.1",
23+
]
24+
1925
[project.urls]
2026
Homepage = "https://github.com/justyns/llm-tools-searxng"
2127
Changelog = "https://github.com/justyns/llm-tools-searxng/releases"

tests/test_llm_tools_searxng.py

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import llm
21
import json
3-
import pytest
42
import os
3+
4+
import llm
5+
import pytest
6+
57
from llm_tools_searxng import SearXNG, searxng_search
68

79

@@ -10,7 +12,7 @@ def test_searxng_search_function_get(httpx_mock, monkeypatch):
1012
# Set required environment variable and explicitly set GET method
1113
monkeypatch.setenv("SEARXNG_URL", "https://searx.be")
1214
monkeypatch.setenv("SEARXNG_METHOD", "GET")
13-
15+
1416
# Mock the HTTP response
1517
mock_response = {
1618
"query": "test query",
@@ -19,16 +21,16 @@ def test_searxng_search_function_get(httpx_mock, monkeypatch):
1921
"title": "Test Result",
2022
"url": "https://example.com",
2123
"content": "This is a test result",
22-
"engine": "duckduckgo"
24+
"engine": "duckduckgo",
2325
}
24-
]
26+
],
2527
}
2628
httpx_mock.add_response(
2729
url="https://searx.be/search?q=test+query&format=json&language=en&pageno=1&safesearch=1",
2830
json=mock_response,
29-
method="GET"
31+
method="GET",
3032
)
31-
33+
3234
model = llm.get_model("echo")
3335
chain_response = model.chain(
3436
json.dumps(
@@ -42,7 +44,7 @@ def test_searxng_search_function_get(httpx_mock, monkeypatch):
4244
)
4345
responses = list(chain_response.responses())
4446
tool_results = json.loads(responses[-1].text())["tool_results"]
45-
47+
4648
# The output should be a JSON string containing the formatted results
4749
output = json.loads(tool_results[0]["output"])
4850
assert output["query"] == "test query"
@@ -55,24 +57,22 @@ def test_searxng_search_function_post(httpx_mock, monkeypatch):
5557
# Set required environment variable and POST method
5658
monkeypatch.setenv("SEARXNG_URL", "https://searx.be")
5759
monkeypatch.setenv("SEARXNG_METHOD", "POST")
58-
60+
5961
mock_response = {
6062
"query": "test query",
6163
"results": [
6264
{
6365
"title": "Test Result POST",
6466
"url": "https://example.com",
6567
"content": "This is a test result via POST",
66-
"engine": "duckduckgo"
68+
"engine": "duckduckgo",
6769
}
68-
]
70+
],
6971
}
7072
httpx_mock.add_response(
71-
url="https://searx.be/search",
72-
json=mock_response,
73-
method="POST"
73+
url="https://searx.be/search", json=mock_response, method="POST"
7474
)
75-
75+
7676
model = llm.get_model("echo")
7777
chain_response = model.chain(
7878
json.dumps(
@@ -86,7 +86,7 @@ def test_searxng_search_function_post(httpx_mock, monkeypatch):
8686
)
8787
responses = list(chain_response.responses())
8888
tool_results = json.loads(responses[-1].text())["tool_results"]
89-
89+
9090
# The output should be a JSON string containing the formatted results
9191
output = json.loads(tool_results[0]["output"])
9292
assert output["query"] == "test query"
@@ -98,28 +98,28 @@ def test_searxng_class_direct_get(httpx_mock, monkeypatch):
9898
"""Test the SearXNG class directly with GET method"""
9999
# Explicitly set GET method
100100
monkeypatch.setenv("SEARXNG_METHOD", "GET")
101-
101+
102102
mock_response = {
103103
"query": "python",
104104
"results": [
105105
{
106106
"title": "Python.org",
107107
"url": "https://python.org",
108108
"content": "The official Python website",
109-
"engine": "google"
109+
"engine": "google",
110110
}
111-
]
111+
],
112112
}
113113
httpx_mock.add_response(
114114
url="https://custom.searxng.com/search?q=python&format=json&language=en&pageno=1&safesearch=1",
115115
json=mock_response,
116-
method="GET"
116+
method="GET",
117117
)
118-
118+
119119
# Test the SearXNG class directly
120120
searxng = SearXNG("https://custom.searxng.com")
121121
result = searxng.search("python")
122-
122+
123123
output = json.loads(result)
124124
assert output["query"] == "python"
125125
assert len(output["results"]) == 1
@@ -135,20 +135,18 @@ def test_searxng_class_direct_post_default(httpx_mock):
135135
"title": "Python.org",
136136
"url": "https://python.org",
137137
"content": "The official Python website",
138-
"engine": "google"
138+
"engine": "google",
139139
}
140-
]
140+
],
141141
}
142142
httpx_mock.add_response(
143-
url="https://custom.searxng.com/search",
144-
json=mock_response,
145-
method="POST"
143+
url="https://custom.searxng.com/search", json=mock_response, method="POST"
146144
)
147-
145+
148146
# Test the SearXNG class directly without setting method (should default to POST)
149147
searxng = SearXNG("https://custom.searxng.com")
150148
result = searxng.search("python")
151-
149+
152150
output = json.loads(result)
153151
assert output["query"] == "python"
154152
assert len(output["results"]) == 1

0 commit comments

Comments
 (0)