Skip to content
Merged
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
1 change: 0 additions & 1 deletion .rust-file-sizes.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"crates/agentic-server-core/src/storage/schema.rs": 552,
"crates/agentic-server-core/src/tool/registry.rs": 515,
"crates/agentic-server-core/src/tool/tool_search.rs": 1764,
"crates/agentic-server-core/src/tool/web_search/mod.rs": 535,
"crates/agentic-server-core/src/types/io/input.rs": 629,
"crates/agentic-server-core/src/types/io/output.rs": 1110,
"crates/agentic-server-core/src/types/request_response.rs": 582,
Expand Down
3 changes: 2 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,8 @@ declaration until they have a complete handler and execution path.
(`CodexNamespaceHandler`), and `tool_search.rs` (`ToolSearchHandler`). Their calls
are returned for the client to resolve; the gateway does not execute them.
- **Gateway-owned / built-in** tools implement both traits: see `web_search/mod.rs`
(`WebSearchHandler`, backed by You.com) and `mcp/handler.rs` (`McpHandler`, backed
(`WebSearchHandler`, backed by the configured `WebSearchProvider` in `web_search/you.rs`
or `web_search/brave.rs`) and `mcp/handler.rs` (`McpHandler`, backed
by `mcp/client.rs`'s MCP protocol client and `mcp/pool.rs`'s connection pool). They
have no client translator association because the gateway owns their execution and
public lifecycle.
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

All notable changes to Agentic API are documented here.

## [Unreleased]

### Added

- Added Brave Search as a selectable backend for the gateway-owned `web_search` tool (#294, Phase 2 of #291).
Select it with `AGENTIC_WEB_SEARCH_PROVIDER=brave` or `[web_search] provider = "brave"` and supply `BRAVE_API_KEY`;
the endpoint defaults to `https://api.search.brave.com` and can be overridden with `AGENTIC_WEB_SEARCH_BASE_URL`
or `[web_search] base_url`. Web and news results come from one request per query. The gateway adapts the shared
tool contract: `allowed_domains` / `blocked_domains` and the model's `include_domains` / `exclude_domains` are
enforced client-side on a label boundary, `count` is clamped to Brave's maximum of 20, `freshness` is rendered in
Brave syntax, `language` maps to `search_lang`, and the You.com-specific `livecrawl`, `livecrawl_formats`,
`crawl_timeout`, and `boost_domains` arguments are ignored. Rejected credentials and HTTP 429 responses fail the
`web_search_call` without an automatic retry, naming the key variable or the upstream `Retry-After` value and never
echoing the secret. Each Brave `metadata[]` entry carries `"provider": "brave"`.
- Added `[web_search] max_concurrent_queries` and `AGENTIC_WEB_SEARCH_MAX_CONCURRENT_QUERIES` to cap concurrent
provider requests inside one batched search. Brave defaults to `1` for its free-plan rate limit; You.com keeps
inheriting `max_concurrent_gateway_calls`. The effective ceiling is the smallest of the gateway limit, this
override, and the provider's own ceiling.

### Changed

- `WebSearchProviderConfig` is now `#[non_exhaustive]` and gains `provider` and `max_concurrent_queries` fields;
construct it with `WebSearchProviderConfig::new(api_key, base_url)` plus the `with_provider` and
`with_max_concurrent_queries` builders. Downstream crates that built it with a struct literal must switch to the
constructor; field reads and `Default` are unchanged. `WebSearchProviderKind` gains a `Brave` variant, `FromStr`
(case-insensitive), `default_base_url`, `default_max_concurrent_queries`, and `config_name`;
`WebSearchHandler::from_config` builds the handler for the selected provider and `GatewayExecutors::from_config`
uses it. With `provider` unset, You.com behavior, configuration, and model-facing output are unchanged; a generated
`config.toml` now records `provider = "you"` and leaves `api_key_env` unset so provider switches select the matching
default credential variable.

## [0.7.0] - 2026-09-14

### Added
Expand Down
72 changes: 65 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ flowchart LR
## ✨ Key Features

- 🔄 **Stateful conversations**: the server manages history via `previous_response_id`. No client-side message tracking, no replaying full transcripts.
- 🛠️ **Server-side tool execution**: an explicit tool-ownership model (gateway / client / provider) decides exactly what runs where. Web search ships today via [You.com](https://you.com), and the model executes multi-step tool chains automatically.
- 🛠️ **Server-side tool execution**: an explicit tool-ownership model (gateway / client / provider) decides exactly what runs where. Web search ships today via [You.com](https://you.com) or [Brave Search](https://brave.com/search/api/), and the model executes multi-step tool chains automatically.
- 📡 **Every transport**: non-streaming HTTP, server-sent events for token streaming, and full **WebSocket** support for interactive clients.
- 🧰 **Codex-ready**: accepts Codex-shaped Responses traffic out of the box, preserving the tool declarations and response item shapes Codex depends on.
- 🏃 **Background execution**: fire-and-forget requests that keep processing server-side.
Expand Down Expand Up @@ -186,6 +186,13 @@ YOU_API_KEY=<your-you.com-api-key> YOU_API_BASE_URL=<you.com-api-base-url> \
cargo run -p agentic-server -- --llm-api-base http://0.0.0.0:5050
```

Prefer [Brave Search](https://brave.com/search/api/) (it has a free developer plan)? Select it instead:

```bash
AGENTIC_WEB_SEARCH_PROVIDER=brave BRAVE_API_KEY=<your-brave-api-key> \
cargo run -p agentic-server -- --llm-api-base http://0.0.0.0:5050
```

The default database is `~/.agentic-api/agentic_api.db`, so running an installed binary does not create state in the
current directory. Set `AGENTIC_API_HOME` to an absolute directory to move both the default database and user
configuration, or set `DATABASE_URL`/`--db-url` to select a different database.
Expand Down Expand Up @@ -219,8 +226,13 @@ llm_api_base = "http://127.0.0.1:5050"
# database_url = "postgresql://agentic-api@localhost/agentic_api"

[web_search]
# Search backend for the gateway-owned web_search tool: "you" (default) or "brave".
provider = "you"
base_url = "https://api.ydc-index.io"
api_key_env = "YOU_API_KEY"
# Concurrent provider requests inside one batched web-search call; unset uses
# the provider default (Brave: 1, You.com: max_concurrent_gateway_calls).
# max_concurrent_queries = 1

[mcp]
allowed_hosts = ["mcp.example.com"]
Expand Down Expand Up @@ -253,17 +265,63 @@ parsed. Order of precedence is `--max-request-body-size-bytes`, then `AGENTIC_MA
file setting.

`api_key_env` names the process environment variable containing the web-search credential; it does not contain the
credential itself. `YOU_API_BASE_URL`, `AGENTIC_MCP_ALLOWED_HOSTS`, `AGENTIC_MAX_REQUEST_BODY_SIZE_BYTES`, and
`AGENTIC_MAX_CONCURRENT_GATEWAY_CALLS` can override their typed file settings. The concurrency value is a sliding-window
upper bound; handlers may further serialize calls to the same tool name. The MCP allowlist is used only for
request-declared remote MCP URLs; configured `[mcp_servers]` entries are trusted operator configuration.
credential itself. When it is unset, the selected provider's conventional variable is read (`YOU_API_KEY` or
`BRAVE_API_KEY`). Newly generated files leave `api_key_env` unset so changing providers also changes the default
credential variable. `AGENTIC_WEB_SEARCH_PROVIDER`, `AGENTIC_WEB_SEARCH_BASE_URL`, `AGENTIC_WEB_SEARCH_MAX_CONCURRENT_QUERIES`,
`AGENTIC_MCP_ALLOWED_HOSTS`, `AGENTIC_MAX_REQUEST_BODY_SIZE_BYTES`, and `AGENTIC_MAX_CONCURRENT_GATEWAY_CALLS` can
override their typed file settings; `YOU_API_BASE_URL` is still honored as the endpoint override when the provider is
`you`. The concurrency values are sliding-window upper bounds; handlers may further serialize calls to the same tool
name. The MCP allowlist is used only for request-declared remote MCP URLs; configured `[mcp_servers]` entries are trusted
operator configuration.

With that file in place, inject only the secret when starting the server:

```bash
YOU_API_KEY="<your-you.com-api-key>" agentic-server
```

#### Web search providers

The gateway-owned `web_search` tool runs against one configured backend; the model-facing tool schema is the same for
every provider.

| Setting | Environment variable | `config.toml` key | Default |
| :--- | :--- | :--- | :--- |
| Provider | `AGENTIC_WEB_SEARCH_PROVIDER` | `[web_search] provider` | `you` |
| API key | variable named by `api_key_env` | `[web_search] api_key_env` | `YOU_API_KEY` / `BRAVE_API_KEY` |
| Endpoint | `AGENTIC_WEB_SEARCH_BASE_URL` (or `YOU_API_BASE_URL` for You.com) | `[web_search] base_url` | none for You.com; `https://api.search.brave.com` for Brave |
| Concurrent queries | `AGENTIC_WEB_SEARCH_MAX_CONCURRENT_QUERIES` | `[web_search] max_concurrent_queries` | You.com inherits `max_concurrent_gateway_calls`; Brave `1` |

**You.com** (`provider = "you"`) is the default and behaves exactly as before: domain filters are applied by the
provider, `count` accepts 1–100, and the You.com-specific `livecrawl`, `livecrawl_formats`, `crawl_timeout`, and
`boost_domains` arguments are forwarded.

**Brave Search** (`provider = "brave"`) needs only `BRAVE_API_KEY`; its [free plan](https://brave.com/search/api/)
covers local and evaluation deployments. Web and news results come from one request per query. The gateway adapts
the shared tool contract to Brave:

- `allowed_domains` / `blocked_domains` (and the model's `include_domains` / `exclude_domains`) are enforced by the
gateway after the response arrives, since Brave has no server-side domain filter. An allowlist is a hard contract,
so a filtered search can return fewer than `count` results.
- `count` is clamped to Brave's maximum of 20; `freshness` is translated to Brave's `pd`/`pw`/`pm`/`py` codes or
passed through as a date range; `language` maps to `search_lang`.
- The You.com-specific arguments above are ignored (logged at debug level).
- Each per-query `metadata[]` entry carries `"provider": "brave"` so the model can see which backend answered.
- Brave's free plan allows roughly one request per second, so batched queries run one at a time by default. Raise
`max_concurrent_queries` on a paid plan. A rate-limited request (HTTP 429) fails that `web_search_call` without an
automatic retry and reports the upstream `Retry-After` value; a cap lowers, but cannot eliminate, 429s on a
per-second quota.

If you switch an existing deployment to Brave, update `api_key_env` if an older `config.toml` pins it (or
remove it) and drop a You.com `base_url`; a mismatched key variable is reported in the failed `web_search_call`
message. Example:

```toml
[web_search]
provider = "brave"
api_key_env = "BRAVE_API_KEY"
```

Restrict the file to the service account (for example, `chmod 600 ~/.agentic-api/config.toml`), especially if you add
credentialed `database_url`, MCP headers, or stdio MCP environment values. Prefer `DATABASE_URL`, referenced API-key
environment variables, and a deployment secret manager for secrets.
Expand Down Expand Up @@ -362,8 +420,8 @@ Claude Code's own tools (Bash, Edit, Read, …) stay **client-owned** — Claude
### Running Claude Code's web search on the gateway

Current Claude Code versions declare Anthropic's native `web_search_20250305` server tool. Agentic API translates that
declaration for the upstream model and executes the resulting search server-side against the configured search backend;
no MCP server or tool alias is required:
declaration for the upstream model and executes the resulting search server-side against the configured search backend
(You.com or Brave Search, see [Web search providers](#web-search-providers)); no MCP server or tool alias is required:

```bash
YOU_API_KEY=<you.com-key> YOU_API_BASE_URL=<you.com-base-url> \
Expand Down
Loading
Loading