Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions docs/advanced/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,29 @@ affecting the agents that do the actual testing.
</ParamField>

<ParamField path="EXA_API_KEY" type="string">
API key for Exa. Enables real-time web search through the Exa `/search` endpoint. Exa also powers the `web_get_contents` tool, which fetches the full text of a page through the Exa `/contents` endpoint. This is the preferred web search provider.
API key for Exa. Enables real-time web search through the Exa `/search` endpoint. Exa powers the `web_get_contents` tool through the Exa `/contents` endpoint unless Parallel is pinned. This is the preferred web search provider.
</ParamField>

<ParamField path="STRIX_WEB_SEARCH_PROVIDER" default="auto" type="string">
Web search provider: `auto`, `perplexity`, or `exa`. With `auto`, Strix uses Exa when `EXA_API_KEY` is set, and Perplexity otherwise. Set an explicit provider to pin one when you configure both keys.
Web search provider: `auto`, `perplexity`, `exa`, or `parallel`. With `auto`, Strix uses Exa when `EXA_API_KEY` is set, and Perplexity otherwise. Set an explicit provider to pin one. Parallel is opt-in and must be selected explicitly.
</ParamField>

<ParamField path="PARALLEL_API_KEY" type="string">
API key for [Parallel Search](https://docs.parallel.ai/search/search-quickstart).
Set `STRIX_WEB_SEARCH_PROVIDER=parallel` to use it for the built-in `web_search` tool.
Requests use `/v1/search` in `fast` mode, returning up to five source URLs with
relevant excerpts. Excerpts are shortened to keep output within 12,000 characters
plus a truncation notice. Source titles and URLs are always preserved, even if
those headers alone exceed the budget.
Queries may contain up to 5,000 characters; the full query is the search objective,
with its first 200 characters also supplied as a search query.
This uses the authenticated API, not the free Search MCP. When Parallel is pinned,
`web_get_contents` uses the Parallel `/v1/extract` endpoint with the same key.

```bash
export STRIX_WEB_SEARCH_PROVIDER="parallel"
export PARALLEL_API_KEY="your-parallel-api-key"
```
</ParamField>

<ParamField path="STRIX_EXA_SEARCH_TYPE" default="auto" type="string">
Expand Down Expand Up @@ -235,7 +253,7 @@ strix --target ./app --config /path/to/config.json
export STRIX_LLM="openrouter/z-ai/glm-5.3"
export LLM_API_KEY="sk-..."

# Optional: Enable web search (Exa preferred, Perplexity supported)
# Optional: Enable web search (Exa preferred; Perplexity and opt-in Parallel supported)
export EXA_API_KEY="..."
export PERPLEXITY_API_KEY="pplx-..."

Expand Down
2 changes: 1 addition & 1 deletion docs/tools/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ Strix agents use specialized tools to test your applications like a real penetra
| -------------- | ---------------------------------------- |
| Python Runtime | Write and execute custom exploit scripts |
| File Editor | Read and modify source code |
| Web Search | Real-time OSINT with Exa or Perplexity |
| Web Search | Real-time OSINT with Exa, Perplexity, or Parallel |
| Notes | Document findings during the scan |
| Reporting | Generate vulnerability reports with PoCs |
12 changes: 11 additions & 1 deletion strix/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class TelemetrySettings(BaseSettings):
run_record_timings: bool = Field(default=False, alias="STRIX_RUN_RECORD_TIMINGS")


WebSearchProvider = Literal["auto", "perplexity", "exa"]
WebSearchProvider = Literal["auto", "perplexity", "exa", "parallel"]
ExaSearchType = Literal["auto", "fast", "instant", "deep-lite", "deep", "deep-reasoning"]


Expand All @@ -196,6 +196,11 @@ class IntegrationSettings(BaseSettings):
alias="EXA_API_KEY",
repr=False,
)
parallel_api_key: str | None = Field(
default=None,
alias="PARALLEL_API_KEY",
repr=False,
)
web_search_provider: WebSearchProvider = Field(
default="auto",
alias="STRIX_WEB_SEARCH_PROVIDER",
Expand All @@ -216,6 +221,11 @@ class IntegrationSettings(BaseSettings):
repr=False,
)

@field_validator("web_search_provider", "exa_search_type", mode="before")
@classmethod
def _normalize_case(cls, value: object) -> object:
return _lowercase(value)


class ViewerSettings(BaseSettings):
model_config = _BASE_CONFIG
Expand Down
14 changes: 12 additions & 2 deletions strix/interface/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def _missing_web_search_vars(integrations: IntegrationSettings) -> list[str]:
return [] if integrations.exa_api_key else ["EXA_API_KEY"]
if integrations.web_search_provider == "perplexity":
return [] if integrations.perplexity_api_key else ["PERPLEXITY_API_KEY"]
if integrations.web_search_provider == "parallel":
return [] if integrations.parallel_api_key else ["PARALLEL_API_KEY"]
if integrations.exa_api_key or integrations.perplexity_api_key:
return []
return ["EXA_API_KEY", "PERPLEXITY_API_KEY"]
Expand All @@ -34,8 +36,8 @@ def _missing_web_search_vars(integrations: IntegrationSettings) -> list[str]:
def validate_environment() -> None:
logger.info("Validating environment")
console = Console()
missing_required_vars = []
missing_optional_vars = []
missing_required_vars: list[str] = []
missing_optional_vars: list[str] = []

settings = load_settings()

Expand Down Expand Up @@ -111,6 +113,10 @@ def validate_environment() -> None:
" - API key for Exa web search (enables real-time research)\n",
style="white",
)
elif var == "PARALLEL_API_KEY":
error_text.append("• ", style="white")
error_text.append("PARALLEL_API_KEY", style="bold cyan")
error_text.append(" - API key for Parallel web search\n", style="white")
elif var == "STRIX_REASONING_EFFORT":
error_text.append("• ", style="white")
error_text.append("STRIX_REASONING_EFFORT", style="bold cyan")
Expand All @@ -137,6 +143,10 @@ def validate_environment() -> None:
)
elif var == "EXA_API_KEY":
error_text.append("export EXA_API_KEY='your-exa-key-here'\n", style="dim white")
elif var == "PARALLEL_API_KEY":
error_text.append(
"export PARALLEL_API_KEY='your-parallel-key-here'\n", style="dim white"
)
elif var == "STRIX_REASONING_EFFORT":
error_text.append(
"export STRIX_REASONING_EFFORT='high'\n",
Expand Down
Loading