google-adk-scavio 0.1.0__tar.gz

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.
@@ -0,0 +1,44 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ pypi:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+
18
+ - name: Check if version is new
19
+ id: check
20
+ run: |
21
+ NAME=$(grep -m1 '^name' pyproject.toml | sed -E 's/.*"(.*)".*/\1/')
22
+ VERSION=$(grep -m1 '^version' pyproject.toml | sed -E 's/.*"(.*)".*/\1/')
23
+ echo "version=$VERSION"
24
+ if curl -fsSL "https://pypi.org/pypi/$NAME/$VERSION/json" > /dev/null 2>&1; then
25
+ echo "new=false" >> "$GITHUB_OUTPUT"
26
+ echo "$NAME $VERSION already on PyPI, skipping"
27
+ else
28
+ echo "new=true" >> "$GITHUB_OUTPUT"
29
+ fi
30
+
31
+ - name: Install build tools
32
+ if: steps.check.outputs.new == 'true'
33
+ run: pip install build twine
34
+
35
+ - name: Build
36
+ if: steps.check.outputs.new == 'true'
37
+ run: python -m build
38
+
39
+ - name: Publish to PyPI
40
+ if: steps.check.outputs.new == 'true'
41
+ env:
42
+ TWINE_USERNAME: __token__
43
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
44
+ run: twine upload dist/*
@@ -0,0 +1,5 @@
1
+ .venv/
2
+ dist/
3
+ __pycache__/
4
+ *.egg-info/
5
+ .pytest_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Scavio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: google-adk-scavio
3
+ Version: 0.1.0
4
+ Summary: Scavio real-time search tools (Google, YouTube, Amazon, Walmart, Reddit) for Google ADK (Agent Development Kit)
5
+ Project-URL: Homepage, https://scavio.dev
6
+ Project-URL: Repository, https://github.com/scavio-ai/google-adk-scavio
7
+ Project-URL: Documentation, https://scavio.dev/docs/google-adk
8
+ Author-email: Scavio <support@scavio.dev>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: adk,agent-development-kit,agents,google-adk,scavio,search,serp,tavily-alternative
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: google-adk>=1.0
14
+ Requires-Dist: scavio>=0.5.0
15
+ Provides-Extra: test
16
+ Requires-Dist: pytest>=7.0; extra == 'test'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # google-adk-scavio
20
+
21
+ [Scavio](https://scavio.dev) real-time search tools for [Google ADK](https://google.github.io/adk-docs/) (Agent Development Kit) — Google, YouTube, Amazon, Walmart, and Reddit, with one API key.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install google-adk-scavio
27
+ ```
28
+
29
+ ## Setup
30
+
31
+ Get a Scavio API key from the [Scavio Dashboard](https://dashboard.scavio.dev) (new accounts get 50 free signup credits, no credit card). Set `SCAVIO_API_KEY` or pass `api_key=` to the factory.
32
+
33
+ ## Usage
34
+
35
+ ```python
36
+ from google.adk.agents import Agent
37
+ from google_adk_scavio import create_scavio_tools
38
+
39
+ root_agent = Agent(
40
+ name="search_assistant",
41
+ model="gemini-2.5-flash",
42
+ instruction="Search the web, news, shopping sites, and Reddit with the Scavio tools.",
43
+ tools=create_scavio_tools(), # reads SCAVIO_API_KEY
44
+ )
45
+ ```
46
+
47
+ Expose only the providers you need:
48
+
49
+ ```python
50
+ tools = create_scavio_tools(
51
+ enable_google=True,
52
+ enable_reddit=True,
53
+ enable_youtube=False,
54
+ enable_amazon=False,
55
+ enable_walmart=False,
56
+ )
57
+ ```
58
+
59
+ ## Tools
60
+
61
+ `create_scavio_tools()` returns plain typed Python functions; ADK wraps each one in a `FunctionTool` automatically. Each returns the structured Scavio JSON response as a dict; errors come back as `{"error": "..."}` rather than raising, so a failed call never crashes the run.
62
+
63
+ | Tool | Description | Credits |
64
+ |---|---|---|
65
+ | `scavio_google_search` | Google web results (organic, knowledge graph, AI overview) | 1 |
66
+ | `scavio_google_news` | Google News articles | 1 |
67
+ | `scavio_youtube_search` | YouTube videos, channels, playlists | 1 |
68
+ | `scavio_youtube_metadata` | Full metadata for a video by id | 1 |
69
+ | `scavio_amazon_search` | Amazon product search | 1 |
70
+ | `scavio_amazon_product` | Amazon product details by ASIN | 1 |
71
+ | `scavio_walmart_search` | Walmart product search | 1 |
72
+ | `scavio_walmart_product` | Walmart product details by product id | 1 |
73
+ | `scavio_reddit_search` | Reddit posts or comments search | 2 |
74
+ | `scavio_reddit_post` | Reddit post with threaded comments by URL | 2 |
75
+
76
+ ## Use every endpoint via MCP
77
+
78
+ For the full Scavio API (TikTok, Instagram, Google Maps, Shopping, Trends, and more), point ADK at the hosted MCP server. Requires ADK's MCP extra: `pip install "google-adk[mcp]"`.
79
+
80
+ ```python
81
+ from google.adk.agents import Agent
82
+ from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams
83
+
84
+ scavio = McpToolset(
85
+ connection_params=StreamableHTTPConnectionParams(
86
+ url="https://mcp.scavio.dev/mcp",
87
+ headers={"x-api-key": "sk_live_..."},
88
+ )
89
+ )
90
+
91
+ root_agent = Agent(name="search_assistant", model="gemini-2.5-flash", tools=[scavio])
92
+ ```
93
+
94
+ ## Links
95
+
96
+ - Scavio: https://scavio.dev
97
+ - Docs: https://scavio.dev/docs/google-adk
98
+ - Dashboard: https://dashboard.scavio.dev
99
+
100
+ ## License
101
+
102
+ MIT
@@ -0,0 +1,84 @@
1
+ # google-adk-scavio
2
+
3
+ [Scavio](https://scavio.dev) real-time search tools for [Google ADK](https://google.github.io/adk-docs/) (Agent Development Kit) — Google, YouTube, Amazon, Walmart, and Reddit, with one API key.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install google-adk-scavio
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Get a Scavio API key from the [Scavio Dashboard](https://dashboard.scavio.dev) (new accounts get 50 free signup credits, no credit card). Set `SCAVIO_API_KEY` or pass `api_key=` to the factory.
14
+
15
+ ## Usage
16
+
17
+ ```python
18
+ from google.adk.agents import Agent
19
+ from google_adk_scavio import create_scavio_tools
20
+
21
+ root_agent = Agent(
22
+ name="search_assistant",
23
+ model="gemini-2.5-flash",
24
+ instruction="Search the web, news, shopping sites, and Reddit with the Scavio tools.",
25
+ tools=create_scavio_tools(), # reads SCAVIO_API_KEY
26
+ )
27
+ ```
28
+
29
+ Expose only the providers you need:
30
+
31
+ ```python
32
+ tools = create_scavio_tools(
33
+ enable_google=True,
34
+ enable_reddit=True,
35
+ enable_youtube=False,
36
+ enable_amazon=False,
37
+ enable_walmart=False,
38
+ )
39
+ ```
40
+
41
+ ## Tools
42
+
43
+ `create_scavio_tools()` returns plain typed Python functions; ADK wraps each one in a `FunctionTool` automatically. Each returns the structured Scavio JSON response as a dict; errors come back as `{"error": "..."}` rather than raising, so a failed call never crashes the run.
44
+
45
+ | Tool | Description | Credits |
46
+ |---|---|---|
47
+ | `scavio_google_search` | Google web results (organic, knowledge graph, AI overview) | 1 |
48
+ | `scavio_google_news` | Google News articles | 1 |
49
+ | `scavio_youtube_search` | YouTube videos, channels, playlists | 1 |
50
+ | `scavio_youtube_metadata` | Full metadata for a video by id | 1 |
51
+ | `scavio_amazon_search` | Amazon product search | 1 |
52
+ | `scavio_amazon_product` | Amazon product details by ASIN | 1 |
53
+ | `scavio_walmart_search` | Walmart product search | 1 |
54
+ | `scavio_walmart_product` | Walmart product details by product id | 1 |
55
+ | `scavio_reddit_search` | Reddit posts or comments search | 2 |
56
+ | `scavio_reddit_post` | Reddit post with threaded comments by URL | 2 |
57
+
58
+ ## Use every endpoint via MCP
59
+
60
+ For the full Scavio API (TikTok, Instagram, Google Maps, Shopping, Trends, and more), point ADK at the hosted MCP server. Requires ADK's MCP extra: `pip install "google-adk[mcp]"`.
61
+
62
+ ```python
63
+ from google.adk.agents import Agent
64
+ from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams
65
+
66
+ scavio = McpToolset(
67
+ connection_params=StreamableHTTPConnectionParams(
68
+ url="https://mcp.scavio.dev/mcp",
69
+ headers={"x-api-key": "sk_live_..."},
70
+ )
71
+ )
72
+
73
+ root_agent = Agent(name="search_assistant", model="gemini-2.5-flash", tools=[scavio])
74
+ ```
75
+
76
+ ## Links
77
+
78
+ - Scavio: https://scavio.dev
79
+ - Docs: https://scavio.dev/docs/google-adk
80
+ - Dashboard: https://dashboard.scavio.dev
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,6 @@
1
+ """Scavio search tools for Google ADK (Agent Development Kit)."""
2
+
3
+ from .tools import create_scavio_tools
4
+
5
+ __all__ = ["create_scavio_tools"]
6
+ __version__ = "0.1.0"
@@ -0,0 +1,179 @@
1
+ """Scavio search tools for Google ADK (Agent Development Kit).
2
+
3
+ Build the tools with `create_scavio_tools()` and pass them to an ADK Agent:
4
+
5
+ from google.adk.agents import Agent
6
+ from google_adk_scavio import create_scavio_tools
7
+
8
+ agent = Agent(name="search_assistant", model="gemini-2.5-flash", tools=create_scavio_tools())
9
+
10
+ The factory returns plain Python functions with typed parameters and docstrings;
11
+ ADK wraps each one in a FunctionTool automatically. Each provider is gated by an
12
+ ``enable_*`` flag. Tools return the Scavio JSON response as a dict.
13
+ """
14
+
15
+ import os
16
+ from typing import Any, Callable, Dict, Optional
17
+
18
+ try:
19
+ from scavio import ScavioClient
20
+ except ImportError as exc: # pragma: no cover
21
+ raise ImportError("`scavio` not installed. Please install using `pip install scavio`") from exc
22
+
23
+
24
+ def _run(call: Callable[[], Dict[str, Any]]) -> Dict[str, Any]:
25
+ """Run a Scavio SDK call, returning its JSON dict or an {"error": ...} dict."""
26
+ try:
27
+ return call()
28
+ except Exception as exc: # noqa: BLE001 - surface errors to the agent
29
+ return {"error": str(exc)}
30
+
31
+
32
+ def create_scavio_tools(
33
+ api_key: Optional[str] = None,
34
+ *,
35
+ enable_google: bool = True,
36
+ enable_youtube: bool = True,
37
+ enable_amazon: bool = True,
38
+ enable_walmart: bool = True,
39
+ enable_reddit: bool = True,
40
+ ) -> list:
41
+ """Build Scavio search tools for a Google ADK Agent.
42
+
43
+ Args:
44
+ api_key: Scavio API key. Falls back to the SCAVIO_API_KEY env var.
45
+ enable_google: Register the Google tools. Defaults to True.
46
+ enable_youtube: Register the YouTube tools. Defaults to True.
47
+ enable_amazon: Register the Amazon tools. Defaults to True.
48
+ enable_walmart: Register the Walmart tools. Defaults to True.
49
+ enable_reddit: Register the Reddit tools. Defaults to True.
50
+
51
+ Returns:
52
+ A list of plain functions ready to pass as ``tools=`` to an ADK Agent.
53
+ """
54
+ client = ScavioClient(api_key=api_key or os.getenv("SCAVIO_API_KEY"))
55
+ tools: list = []
56
+
57
+ if enable_google:
58
+ def scavio_google_search(query: str, gl: Optional[str] = None, hl: Optional[str] = None) -> dict:
59
+ """Search Google for real-time web results (organic results, knowledge graph, AI overview). Costs 1 credit.
60
+
61
+ Args:
62
+ query: The search query.
63
+ gl: Two-letter ISO country code for the search, e.g. us.
64
+ hl: Two-letter language code for the results, e.g. en.
65
+ """
66
+ _p = {"gl": gl, "hl": hl}
67
+ return _run(lambda: client.google.search(query, **{k: v for k, v in _p.items() if v is not None}))
68
+ tools.append(scavio_google_search)
69
+
70
+ def scavio_google_news(query: str, gl: Optional[str] = None, hl: Optional[str] = None) -> dict:
71
+ """Search Google News for current news articles on a topic. Costs 1 credit.
72
+
73
+ Args:
74
+ query: The news search query.
75
+ gl: Two-letter ISO country code for the search, e.g. us.
76
+ hl: Two-letter language code for the results, e.g. en.
77
+ """
78
+ _p = {"query": query, "gl": gl, "hl": hl}
79
+ return _run(lambda: client.google.news(**{k: v for k, v in _p.items() if v is not None}))
80
+ tools.append(scavio_google_news)
81
+
82
+ if enable_youtube:
83
+ def scavio_youtube_search(query: str, upload_date: Optional[str] = None, type: Optional[str] = None, duration: Optional[str] = None, sort_by: Optional[str] = None) -> dict:
84
+ """Search YouTube for videos, channels, or playlists. Costs 1 credit.
85
+
86
+ Args:
87
+ query: The video search query.
88
+ upload_date: Upload date filter: last_hour, today, this_week, this_month, this_year.
89
+ type: Result type: video, channel, playlist.
90
+ duration: Duration filter: short, medium, long.
91
+ sort_by: Sort order: relevance, date, view_count, rating.
92
+ """
93
+ _p = {"upload_date": upload_date, "type": type, "duration": duration, "sort_by": sort_by}
94
+ return _run(lambda: client.youtube.search(query, **{k: v for k, v in _p.items() if v is not None}))
95
+ tools.append(scavio_youtube_search)
96
+
97
+ def scavio_youtube_metadata(video_id: str) -> dict:
98
+ """Fetch full metadata for a YouTube video by id. Costs 1 credit.
99
+
100
+ Args:
101
+ video_id: YouTube video id, e.g. dQw4w9WgXcQ.
102
+ """
103
+ return _run(lambda: client.youtube.metadata(video_id))
104
+ tools.append(scavio_youtube_metadata)
105
+
106
+ if enable_amazon:
107
+ def scavio_amazon_search(query: str, domain: Optional[str] = None, country: Optional[str] = None, sort_by: Optional[str] = None, start_page: Optional[int] = None) -> dict:
108
+ """Search Amazon for products matching a query. Costs 1 credit.
109
+
110
+ Args:
111
+ query: The product search query.
112
+ domain: Amazon domain suffix, e.g. com or co.uk.
113
+ country: Two-letter country code for localization.
114
+ sort_by: Sort order: featured, price_low_to_high, price_high_to_low, average_review, most_recent, bestsellers.
115
+ start_page: First result page to return (1-based).
116
+ """
117
+ _p = {"domain": domain, "country": country, "sort_by": sort_by, "start_page": start_page}
118
+ return _run(lambda: client.amazon.search(query, **{k: v for k, v in _p.items() if v is not None}))
119
+ tools.append(scavio_amazon_search)
120
+
121
+ def scavio_amazon_product(asin: str, domain: Optional[str] = None, country: Optional[str] = None) -> dict:
122
+ """Fetch full Amazon product details by ASIN. Costs 1 credit.
123
+
124
+ Args:
125
+ asin: Amazon Standard Identification Number, e.g. B09XS7JWHH.
126
+ domain: Amazon domain suffix, e.g. com or co.uk.
127
+ country: Two-letter country code for localization.
128
+ """
129
+ _p = {"domain": domain, "country": country}
130
+ return _run(lambda: client.amazon.product(asin, **{k: v for k, v in _p.items() if v is not None}))
131
+ tools.append(scavio_amazon_product)
132
+
133
+ if enable_walmart:
134
+ def scavio_walmart_search(query: str, sort_by: Optional[str] = None, min_price: Optional[int] = None, max_price: Optional[int] = None, start_page: Optional[int] = None) -> dict:
135
+ """Search Walmart for products matching a query. Costs 1 credit.
136
+
137
+ Args:
138
+ query: The product search query.
139
+ sort_by: Sort order: best_match, price_low, price_high, best_seller.
140
+ min_price: Minimum price filter in USD.
141
+ max_price: Maximum price filter in USD.
142
+ start_page: First result page to return (1-based).
143
+ """
144
+ _p = {"sort_by": sort_by, "min_price": min_price, "max_price": max_price, "start_page": start_page}
145
+ return _run(lambda: client.walmart.search(query, **{k: v for k, v in _p.items() if v is not None}))
146
+ tools.append(scavio_walmart_search)
147
+
148
+ def scavio_walmart_product(product_id: str) -> dict:
149
+ """Fetch full Walmart product details by product id. Costs 1 credit.
150
+
151
+ Args:
152
+ product_id: Walmart product id.
153
+ """
154
+ return _run(lambda: client.walmart.product(product_id))
155
+ tools.append(scavio_walmart_product)
156
+
157
+ if enable_reddit:
158
+ def scavio_reddit_search(query: str, type: Optional[str] = None, sort: Optional[str] = None) -> dict:
159
+ """Search Reddit posts or comments. Costs 2 credits.
160
+
161
+ Args:
162
+ query: The Reddit search query.
163
+ type: Result type: posts or comments.
164
+ sort: Sort order: relevance, new, hot, top, comments.
165
+ """
166
+ _p = {"type": type, "sort": sort}
167
+ return _run(lambda: client.reddit.search(query, **{k: v for k, v in _p.items() if v is not None}))
168
+ tools.append(scavio_reddit_search)
169
+
170
+ def scavio_reddit_post(url: str) -> dict:
171
+ """Fetch a Reddit post and its threaded comments by URL. Costs 2 credits.
172
+
173
+ Args:
174
+ url: Full URL of the Reddit post.
175
+ """
176
+ return _run(lambda: client.reddit.post(url))
177
+ tools.append(scavio_reddit_post)
178
+
179
+ return tools
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "google-adk-scavio"
7
+ version = "0.1.0"
8
+ description = "Scavio real-time search tools (Google, YouTube, Amazon, Walmart, Reddit) for Google ADK (Agent Development Kit)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Scavio", email = "support@scavio.dev" }]
13
+ keywords = ["google-adk", "adk", "agent-development-kit", "agents", "scavio", "search", "serp", "tavily-alternative"]
14
+ dependencies = [
15
+ "google-adk>=1.0",
16
+ "scavio>=0.5.0",
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://scavio.dev"
21
+ Repository = "https://github.com/scavio-ai/google-adk-scavio"
22
+ Documentation = "https://scavio.dev/docs/google-adk"
23
+
24
+ [project.optional-dependencies]
25
+ test = ["pytest>=7.0"]
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["google_adk_scavio"]
@@ -0,0 +1,145 @@
1
+ """Tests for google-adk-scavio. The Scavio SDK client is mocked; no key or network used,
2
+ except the env-gated live test at the bottom."""
3
+
4
+ import os
5
+
6
+ import pytest
7
+
8
+ import google_adk_scavio.tools as tools_mod
9
+ from google_adk_scavio import create_scavio_tools
10
+
11
+ ALL_TOOL_NAMES = {
12
+ "scavio_google_search",
13
+ "scavio_google_news",
14
+ "scavio_youtube_search",
15
+ "scavio_youtube_metadata",
16
+ "scavio_amazon_search",
17
+ "scavio_amazon_product",
18
+ "scavio_walmart_search",
19
+ "scavio_walmart_product",
20
+ "scavio_reddit_search",
21
+ "scavio_reddit_post",
22
+ }
23
+
24
+
25
+ class _Recorder:
26
+ def __init__(self, calls):
27
+ self._calls = calls
28
+
29
+ def __getattr__(self, method):
30
+ def _call(*args, **kwargs):
31
+ self._calls.append((method, args, kwargs))
32
+ return {"ok": True, "method": method, "args": args, "kwargs": kwargs}
33
+
34
+ return _call
35
+
36
+
37
+ class _FakeClient:
38
+ def __init__(self, *args, **kwargs):
39
+ self.calls = []
40
+ for ns in ("google", "youtube", "amazon", "walmart", "reddit"):
41
+ setattr(self, ns, _Recorder(self.calls))
42
+
43
+
44
+ class _RaisingClient:
45
+ def __init__(self, *args, **kwargs):
46
+ class _Boom:
47
+ def __getattr__(self, method):
48
+ def _call(*a, **kw):
49
+ raise RuntimeError("scavio exploded")
50
+
51
+ return _call
52
+
53
+ for ns in ("google", "youtube", "amazon", "walmart", "reddit"):
54
+ setattr(self, ns, _Boom())
55
+
56
+
57
+ def _build(monkeypatch, **kwargs):
58
+ monkeypatch.setattr(tools_mod, "ScavioClient", _FakeClient)
59
+ return create_scavio_tools(api_key="test", **kwargs)
60
+
61
+
62
+ def _get(tools, name):
63
+ return next(t for t in tools if t.__name__ == name)
64
+
65
+
66
+ def test_all_tools_register(monkeypatch):
67
+ tools = _build(monkeypatch)
68
+ names = [t.__name__ for t in tools]
69
+ assert len(names) == len(set(names)) == 10
70
+ assert set(names) == ALL_TOOL_NAMES
71
+
72
+
73
+ def test_provider_gating(monkeypatch):
74
+ tools = _build(
75
+ monkeypatch,
76
+ enable_google=False,
77
+ enable_youtube=False,
78
+ enable_amazon=False,
79
+ enable_walmart=False,
80
+ enable_reddit=True,
81
+ )
82
+ assert {t.__name__ for t in tools} == {"scavio_reddit_search", "scavio_reddit_post"}
83
+
84
+
85
+ def test_google_search_sends_gl_hl(monkeypatch):
86
+ g = _get(_build(monkeypatch), "scavio_google_search")
87
+ out = g("ai agents", gl="fr", hl="fr")
88
+ assert isinstance(out, dict)
89
+ assert out["method"] == "search"
90
+ assert out["args"] == ("ai agents",)
91
+ assert out["kwargs"] == {"gl": "fr", "hl": "fr"}
92
+
93
+
94
+ def test_google_search_drops_none(monkeypatch):
95
+ g = _get(_build(monkeypatch), "scavio_google_search")
96
+ out = g("ai agents")
97
+ assert out["args"] == ("ai agents",)
98
+ assert out["kwargs"] == {}
99
+
100
+
101
+ def test_google_news_sends_query_kwarg(monkeypatch):
102
+ n = _get(_build(monkeypatch), "scavio_google_news")
103
+ out = n("openai", gl="us")
104
+ assert out["method"] == "news"
105
+ assert out["args"] == ()
106
+ assert out["kwargs"] == {"query": "openai", "gl": "us"}
107
+
108
+
109
+ def test_amazon_product_uses_asin(monkeypatch):
110
+ p = _get(_build(monkeypatch), "scavio_amazon_product")
111
+ out = p("B000000000")
112
+ assert out["method"] == "product"
113
+ assert out["args"] == ("B000000000",)
114
+
115
+
116
+ def test_errors_wrapped_not_raised(monkeypatch):
117
+ monkeypatch.setattr(tools_mod, "ScavioClient", _RaisingClient)
118
+ tools = create_scavio_tools(api_key="test")
119
+ g = _get(tools, "scavio_google_search")
120
+ assert g("boom") == {"error": "scavio exploded"}
121
+
122
+
123
+ def test_adk_function_tool_wraps(monkeypatch):
124
+ """ADK must be able to wrap every tool function as a FunctionTool."""
125
+ from google.adk.tools import FunctionTool
126
+
127
+ tools = _build(monkeypatch)
128
+ wrapped = [FunctionTool(t) for t in tools]
129
+ assert {w.name for w in wrapped} == ALL_TOOL_NAMES
130
+ assert all(w.description for w in wrapped)
131
+
132
+
133
+ @pytest.mark.skipif("SCAVIO_API_KEY" not in os.environ, reason="requires live API key")
134
+ def test_live_google_search():
135
+ """One live call against api.scavio.dev (set SCAVIO_API_KEY to run)."""
136
+ tools = create_scavio_tools(
137
+ enable_youtube=False, enable_amazon=False, enable_walmart=False, enable_reddit=False
138
+ )
139
+ g = _get(tools, "scavio_google_search")
140
+ out = g("scavio search api for ai agents", gl="us", hl="en")
141
+ assert "error" not in out, out.get("error")
142
+ assert out.get("organic_results"), out
143
+ first = out["organic_results"][0]
144
+ assert first["link"].startswith("http")
145
+ assert first["title"]