feat(tools): add querit search tool#6493
Conversation
📝 WalkthroughWalkthroughAdds a new ChangesQueritSearchTool implementation and integration
Sequence Diagram(s)sequenceDiagram
participant Agent
participant QueritSearchTool
participant QueritAPI
Agent->>QueritSearchTool: run(query, filters)
QueritSearchTool->>QueritSearchTool: _build_filters(params)
loop up to 3 attempts
QueritSearchTool->>QueritAPI: POST search_url(payload)
QueritAPI-->>QueritSearchTool: response or RequestException
end
QueritSearchTool-->>Agent: raw Querit JSON response
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
lib/crewai-tools/tests/tools/querit_search_tool_test.py (2)
200-210: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider testing runtime
rawoverride.The raw-response test only exercises
raw=Trueset at construction time. The_runmethod has a distinct branch forkwargs["raw"]overridingself.raw. A test passingraw=Trueat call time would cover that path.🧪 Suggested test
def test_querit_search_tool_runtime_raw_override() -> None: raw_response = {"results": {"result": {"url": "https://example.com"}}} tool = QueritSearchTool() with patch( "crewai_tools.tools.querit_search_tool.querit_search_tool.requests.post", return_value=_mock_response(raw_response), ): result = tool.run(query="AI news", raw=True) assert result == raw_response🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py` around lines 200 - 210, The raw-response test only covers the constructor-level raw=True path, so add a test for the runtime override branch in QueritSearchTool._run. Create a new test alongside test_querit_search_tool_can_return_raw_response that instantiates QueritSearchTool without raw=True, calls run(query="AI news", raw=True), and asserts the raw response is returned, so kwargs["raw"] overriding self.raw is covered.
175-190: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding a test for the all-retries-fail path.
The retry test only covers the success-on-third-attempt scenario. A test verifying that the tool re-raises the last
RequestExceptionafter all 3 attempts fail would close the gap on theif response is None: raise last_errorbranch.🧪 Suggested test
def test_querit_search_tool_raises_after_all_retries_fail() -> None: tool = QueritSearchTool() with patch( "crewai_tools.tools.querit_search_tool.querit_search_tool.requests.post", side_effect=requests.ConnectTimeout("timeout"), ) as post: with pytest.raises(requests.ConnectTimeout, match="timeout"): tool.run(query="AI news") assert post.call_count == 3🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py` around lines 175 - 190, The retry coverage in QueritSearchTool is incomplete: the existing test only verifies success after retries, but not the branch where all attempts fail and the last RequestException is re-raised. Add a new test alongside test_querit_search_tool_retries_request_exceptions_three_times() that patches requests.post to keep raising the same timeout exception for all three attempts, then assert tool.run(query="AI news") raises that exception and that the post mock was called three times.lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py (1)
156-177: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winRetry loop doesn't cover HTTP error responses and lacks backoff.
raise_for_status()is called after the loop (line 177), so transient HTTP errors (429, 500, 502, 503) are not retried — only transport-levelRequestExceptions fromrequests.postare. Additionally, retries fire immediately with no delay, which can worsen congestion on an already-struggling endpoint.Consider moving
raise_for_status()inside the retry loop (optionally only retrying on 429/5xx) and adding a small backoff between attempts.♻️ Proposed refactor
response: requests.Response | None = None last_error: requests.RequestException | None = None - for _ in range(3): + import time + for attempt in range(3): try: response = requests.post( self.search_url, headers={ "Accept": "application/json", "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json", }, json=payload, timeout=self.timeout, ) + response.raise_for_status() break except requests.RequestException as error: last_error = error - if response is None: + if attempt < 2: + time.sleep(2 ** attempt) if response is None: if last_error is not None: raise last_error raise RuntimeError("Querit request failed without an exception") - response.raise_for_status() search_response = response.json()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py` around lines 156 - 177, The retry logic in querit_search_tool’s request flow only retries transport exceptions because response.raise_for_status() happens after the loop, so HTTP 429/5xx responses are never retried. Update the request handling in the search method around requests.post and response.raise_for_status() so status-code failures are evaluated inside the retry loop, and only reattempt on transient HTTP errors such as 429 and 5xx. Also add a small backoff delay between attempts in the same loop so the Querit endpoint isn’t hit repeatedly with no pause.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py`:
- Around line 266-271: The `searchParameters` dict in `QueritSearchTool` is
allowing `**query_context` to overwrite the explicit `query` and `count` values,
so adjust the merge order or build the object in a way that preserves the
original inputs. Update the normalization logic in the `return` block that
constructs `searchParameters` so `query` and `count` always come from the method
arguments, and only non-conflicting fields from `query_context` are added. If
needed, tighten the test around `QueritSearchTool` to verify `query` and `count`
are not replaced by values from `query_context`.
---
Nitpick comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py`:
- Around line 156-177: The retry logic in querit_search_tool’s request flow only
retries transport exceptions because response.raise_for_status() happens after
the loop, so HTTP 429/5xx responses are never retried. Update the request
handling in the search method around requests.post and
response.raise_for_status() so status-code failures are evaluated inside the
retry loop, and only reattempt on transient HTTP errors such as 429 and 5xx.
Also add a small backoff delay between attempts in the same loop so the Querit
endpoint isn’t hit repeatedly with no pause.
In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py`:
- Around line 200-210: The raw-response test only covers the constructor-level
raw=True path, so add a test for the runtime override branch in
QueritSearchTool._run. Create a new test alongside
test_querit_search_tool_can_return_raw_response that instantiates
QueritSearchTool without raw=True, calls run(query="AI news", raw=True), and
asserts the raw response is returned, so kwargs["raw"] overriding self.raw is
covered.
- Around line 175-190: The retry coverage in QueritSearchTool is incomplete: the
existing test only verifies success after retries, but not the branch where all
attempts fail and the last RequestException is re-raised. Add a new test
alongside test_querit_search_tool_retries_request_exceptions_three_times() that
patches requests.post to keep raising the same timeout exception for all three
attempts, then assert tool.run(query="AI news") raises that exception and that
the post mock was called three times.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e679c743-3b8e-480f-8578-299daf3914b0
📒 Files selected for processing (9)
docs/docs.jsondocs/edge/en/tools/search-research/queritsearchtool.mdxlib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/querit_search_tool/README.mdlib/crewai-tools/src/crewai_tools/tools/querit_search_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.pylib/crewai-tools/tests/tools/querit_search_tool_test.pylib/crewai-tools/tool.specs.json
d991462 to
f7d86ac
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
lib/crewai-tools/tests/tools/querit_search_tool_test.py (1)
172-189: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a test for exhausted retries (all 3 attempts fail).
The current test only covers 2 failures followed by success. There's no test verifying that when all 3 attempts fail, the last exception is propagated to the caller. This is an important edge case for the retry logic.
🧪 Proposed test
def test_querit_search_tool_raises_after_exhausting_retries() -> None: """Verify that the last exception is raised when all retries are exhausted.""" tool = QueritSearchTool() with patch( "crewai_tools.tools.querit_search_tool.querit_search_tool.requests.post", side_effect=requests.ConnectTimeout("timeout"), ) as post: with pytest.raises(requests.ConnectTimeout, match="timeout"): tool.run(query="AI news") assert post.call_count == 3🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py` around lines 172 - 189, Add a test that covers the exhausted retry path in QueritSearchTool’s request handling. Extend the existing retry tests around tool.run and the requests.post patch to simulate all three attempts failing with a ConnectTimeout, then assert the last exception is propagated with pytest.raises and that post.call_count is 3. Use the existing test pattern in test_querit_search_tool_retries_request_exceptions_three_times() and the QueritSearchTool.run entrypoint to keep the new test aligned with the current retry logic.lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py (1)
148-162: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winNo backoff between retry attempts.
The loop retries immediately on failure with no delay. This can overwhelm an already-struggling server and is considered an anti-pattern for retry logic. Consider adding exponential backoff (e.g.,
time.sleep(2 ** attempt)or usingurllib3.util.Retry).⏱️ Proposed fix with exponential backoff
+import time + for attempt in range(3): try: response = requests.post( self.search_url, headers={ "Accept": "application/json", "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json", }, json=payload, timeout=self.timeout, ) break except requests.RequestException as error: last_error = error + if attempt < 2: + time.sleep(2 ** attempt)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py` around lines 148 - 162, In the retry loop inside querit_search_tool.py’s search request logic, failures are retried immediately with no pause, so add exponential backoff between attempts in the same block that catches requests.RequestException. Use the loop attempt index in the retry delay (for example, increasing wait time per retry) or switch the request path to a Retry-based approach, and keep the change localized around the requests.post call and last_error handling so the search behavior remains the same aside from the delay.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py`:
- Around line 146-167: The retry logic in `QueritSearchTool` only retries
`requests.post`, so transient HTTP failures like 5xx and 429 from
`response.raise_for_status()` are not retried. Move the `raise_for_status()`
handling inside the retry loop in the `QueritSearchTool` request flow, and make
`HTTPError` retryable there (ideally only for retryable status codes such as 429
and 5xx) while preserving the existing `last_error` behavior for final failure.
In `@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/README.md`:
- Around line 63-76: The Arguments section in the Querit search tool README is
missing the public raw option, so update the documentation to include raw
alongside the existing inputs. Add a brief entry for raw in the same style as
query, count, and chunksPerDoc, and keep the wording aligned with the tool’s
public API so users can discover the raw-response mode.
---
Nitpick comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py`:
- Around line 148-162: In the retry loop inside querit_search_tool.py’s search
request logic, failures are retried immediately with no pause, so add
exponential backoff between attempts in the same block that catches
requests.RequestException. Use the loop attempt index in the retry delay (for
example, increasing wait time per retry) or switch the request path to a
Retry-based approach, and keep the change localized around the requests.post
call and last_error handling so the search behavior remains the same aside from
the delay.
In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py`:
- Around line 172-189: Add a test that covers the exhausted retry path in
QueritSearchTool’s request handling. Extend the existing retry tests around
tool.run and the requests.post patch to simulate all three attempts failing with
a ConnectTimeout, then assert the last exception is propagated with
pytest.raises and that post.call_count is 3. Use the existing test pattern in
test_querit_search_tool_retries_request_exceptions_three_times() and the
QueritSearchTool.run entrypoint to keep the new test aligned with the current
retry logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c8f31f03-471c-4ccd-9e22-614eb1ae7a10
📒 Files selected for processing (9)
docs/docs.jsondocs/edge/en/tools/search-research/queritsearchtool.mdxlib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/querit_search_tool/README.mdlib/crewai-tools/src/crewai_tools/tools/querit_search_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.pylib/crewai-tools/tests/tools/querit_search_tool_test.pylib/crewai-tools/tool.specs.json
✅ Files skipped from review due to trivial changes (3)
- lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/init.py
- docs/docs.json
- docs/edge/en/tools/search-research/queritsearchtool.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
- lib/crewai-tools/tool.specs.json
- lib/crewai-tools/src/crewai_tools/tools/init.py
- lib/crewai-tools/src/crewai_tools/init.py
f7d86ac to
7ab7a06
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
lib/crewai-tools/tests/tools/querit_search_tool_test.py (2)
123-155: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding a test for runtime filter overrides.
All filter tests pass filters via the constructor. The
_build_filtersmethod also accepts runtime kwargs (e.g.,tool.run(query="test", site_include=["foo.com"])), but this path is untested. A small test would verify that runtime overrides are serialized correctly.🧪 Suggested test for runtime filter override
def test_querit_search_tool_accepts_runtime_filter_overrides() -> None: """Verify that runtime filter parameters override constructor defaults.""" tool = QueritSearchTool(site_include=["default.com"]) with patch( "crewai_tools.tools.querit_search_tool.querit_search_tool.requests.post", return_value=_mock_response({"results": {"result": []}}), ) as post: tool.run(query="AI news", site_include=["override.com"]) assert post.call_args.kwargs["json"]["filters"]["sites"]["include"] == ["override.com"]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py` around lines 123 - 155, Add a test that covers runtime filter overrides in QueritSearchTool, since current coverage only verifies constructor-supplied filters. Extend the existing test suite around QueritSearchTool.run and _build_filters to call run(query=..., site_include=...) with a runtime override, then assert the request payload sent through requests.post serializes the overridden site filter instead of the constructor default. Use the QueritSearchTool and _build_filters symbols to keep the test aligned with the filter-building path.
182-219: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a test for retry exhaustion.
The retry tests verify that the tool succeeds after transient failures, but no test covers the case where all 3 attempts fail. The implementation has a distinct code path (
response is None→raise last_error) that is currently untested. This is a behavior-focused gap, not an implementation-detail concern.🧪 Suggested test for retry exhaustion
def test_querit_search_tool_raises_after_exhausting_retries() -> None: """Verify that the tool re-raises the last error after all retries are exhausted.""" tool = QueritSearchTool() with patch( "crewai_tools.tools.querit_search_tool.querit_search_tool.requests.post", side_effect=[ _mock_http_error_response(503), _mock_http_error_response(502), _mock_http_error_response(500), ], ) as post: with pytest.raises(requests.HTTPError): tool.run(query="AI news") assert post.call_count == 3🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py` around lines 182 - 219, The retry coverage in QueritSearchTool is missing the exhaustion path where all three attempts fail and the last error is re-raised. Add a test alongside test_querit_search_tool_retries_request_exceptions_three_times and test_querit_search_tool_retries_transient_http_errors_three_times that patches requests.post to fail three times, then asserts QueritSearchTool.run raises the final exception and that the post mock was called three times. Use the existing helper behavior around _mock_http_error_response and the retry logic in QueritSearchTool.run to keep the test aligned with the current implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.py`:
- Around line 162-172: The retry loop in querit_search_tool.py keeps the last
failing Response alive after retryable requests, so QueritSearchTool can parse
an error body instead of raising. In the HTTPError branch inside the retry
logic, reset the local response variable to None whenever you store last_error,
so the final response is only treated as successful when a real non-error
response was returned. Keep the existing last_error propagation and the final if
response is None check in QueritSearchTool/querit_search_tool.py.
---
Nitpick comments:
In `@lib/crewai-tools/tests/tools/querit_search_tool_test.py`:
- Around line 123-155: Add a test that covers runtime filter overrides in
QueritSearchTool, since current coverage only verifies constructor-supplied
filters. Extend the existing test suite around QueritSearchTool.run and
_build_filters to call run(query=..., site_include=...) with a runtime override,
then assert the request payload sent through requests.post serializes the
overridden site filter instead of the constructor default. Use the
QueritSearchTool and _build_filters symbols to keep the test aligned with the
filter-building path.
- Around line 182-219: The retry coverage in QueritSearchTool is missing the
exhaustion path where all three attempts fail and the last error is re-raised.
Add a test alongside
test_querit_search_tool_retries_request_exceptions_three_times and
test_querit_search_tool_retries_transient_http_errors_three_times that patches
requests.post to fail three times, then asserts QueritSearchTool.run raises the
final exception and that the post mock was called three times. Use the existing
helper behavior around _mock_http_error_response and the retry logic in
QueritSearchTool.run to keep the test aligned with the current implementation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a9e2fae1-a877-484d-b4b5-5dfd2359cb74
📒 Files selected for processing (9)
docs/docs.jsondocs/edge/en/tools/search-research/queritsearchtool.mdxlib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/querit_search_tool/README.mdlib/crewai-tools/src/crewai_tools/tools/querit_search_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/querit_search_tool/querit_search_tool.pylib/crewai-tools/tests/tools/querit_search_tool_test.pylib/crewai-tools/tool.specs.json
✅ Files skipped from review due to trivial changes (1)
- docs/docs.json
🚧 Files skipped from review as they are similar to previous changes (5)
- lib/crewai-tools/src/crewai_tools/tools/querit_search_tool/init.py
- lib/crewai-tools/src/crewai_tools/tools/init.py
- docs/edge/en/tools/search-research/queritsearchtool.mdx
- lib/crewai-tools/src/crewai_tools/init.py
- lib/crewai-tools/tool.specs.json
Description
Add
QueritSearchToolto allow CrewAI agents to perform real-time web searches through the Querit Search API.This change includes:
QUERIT_API_KEYsupport.query,count, andchunksPerDoc.site_include,site_exclude,time_range,country_include, andlanguage_include.filterspayload.filtersparameter.time_rangeformat:d[number],w[number],m[number],y[number], orYYYY-MM-DDtoYYYY-MM-DD.countto10.chunksPerDocto3.requestsexceptions and retryable HTTP status codes such as429and5xx.tool.specs.json.Related Issues
Closes #
Testing
Describe how you tested these changes:
Tests run:
Result:
Lint and formatting checks:
Result:
Additional validation:
python -m json.tool lib/crewai-tools/tool.specs.json >/dev/nullResult: passed.
Type of Change