Tavily
About 662 wordsAbout 2 min
Adds Tavily’s AI-native web search to Spora agents — search the web and receive an LLM-optimised answer plus ranked source results in one tool call. A free tier is available.
Installation
php bin/spora plugin:install spora-ai/spora-plugin-tavilyFor local development against a sibling checkout, pass --path=/abs/path/to/checkout.
After install, the tool is exposed as tavily_search (visible in php bin/spora plugin:list and the agent UI under Tools).
Configuration
Settings → Tools → Tavily Search. Authentication uses a Bearer token against https://api.tavily.com.
| Setting | Required | Default |
|---|---|---|
api_key | yes | — |
http_timeout | no | 30 (seconds; overridden by SPORA_TOOL_HTTP_TIMEOUT env var) |
api_key is encrypted at rest by Spora’s ToolConfigService, masked in the UI, and never logged (the api_key field in outbound payload logs is redacted as ***).
Per-tool parameters
The plugin ships one tool, tavily_search. It calls Tavily’s POST https://api.tavily.com/search endpoint and returns ToolResult::ok (formatted text) or ToolResult::fail. Never throws — a single API failure cannot kill the agent loop.
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
query | string | yes | — | The exact research question or search query. |
search_depth | string | no | basic | basic or advanced. Advanced traverses deeper but takes longer. |
What it returns
On success, the tool returns a single text block:
Search Results for '<query>':
Summary: <LLM-generated answer, if include_answer is true>
[1] <result title>
URL: <result url>
<result content snippet>
[2] ...Internally the tool sends:
{
"api_key": "<your key>",
"query": "<query>",
"search_depth": "basic",
"include_answer": true
}to https://api.tavily.com/search. The full Tavily /search parameter set (topic, max_results, include_raw_content, time_range, start_date, end_date, include_domains, exclude_domains, country, include_images, auto_parameters, exact_match, safe_search, …) is documented at https://docs.tavily.com/documentation/api-reference/endpoint/search. Exposing them as agent-callable arguments is out of scope for v1; if you need a knob, open an issue.
Tavily account
- Sign up: https://tavily.com
- API key dashboard: https://app.tavily.com/home
- API reference: https://docs.tavily.com
Rate-limit / plan errors come back as HTTP 429 / 432 / 433 and surface as ToolResult::fail with the status code embedded.
Translations
The plugin ships a German catalogue at locales/de.json, declared through TavilyPlugin::translationsPaths(). The catalogue key is the English source string:
{
"Tavily Search": "Tavily-Suche",
"API key for api.tavily.com/search (Optimized for LLMs)": "API-Schlüssel für api.tavily.com/search (für LLMs optimiert)"
}Only operator-facing copy is translated — the tool’s display name, every #[ToolSetting] description this plugin owns, and the plugin description. A description core already translates is core’s to keep: the host merges the plugin’s catalogue under core’s, and core wins a key collision, so the plugin does not repeat it. Strings the model reads (#[Tool].description, #[ToolParameter].description, #[ToolSetting].label and its option labels) stay English, in the operator’s view too: a German tool description degrades the model’s tool selection, and a translated label that also reaches the model puts German into its prompt.
Editing the English orphans the translation — the key changes and the old entry is dead — so re-translate in the same commit. The repository checks its catalogue against three rules: no translation is empty, none is identical to its key, and no key survives a copy edit. The gate is this repository’s own tests/Unit/LocaleCatalogTest.php.
Requires spora-core >= 0.31.0; on older cores the catalogue is ignored.
Development
composer install
./vendor/bin/pest # 4 tests
./vendor/bin/phpstan analyse --no-progress
./vendor/bin/php-cs-fixer fix --dry-run --diff --no-interactionCI: .github/workflows/ci.yml — Pest on PHP 8.4 + 8.5, PHPStan level (per phpstan.neon), php-cs-fixer dry-run. A separate coverage job runs Pest with pcov and uploads coverage.xml + JUnit; the sonar job uploads both to SonarCloud (project key spora-ai_spora-plugin-tavily), so the new_coverage metric is measurable per PR. Requires the SONAR_TOKEN secret in the repo.
Repo: spora-ai/spora-plugin-tavily · MIT