> SpecStream is the live web data engine for teams and agents. This is the Markdown copy of https://specstream.ai/docs/integrations/dr-tulu. Every page, with what SpecStream does and costs: https://specstream.ai/llms.txt

# DR Tulu

Run DR Tulu's web search and page reading on SpecStream by changing one address in each of two files. Your Serper and Jina keys become one SpecStream key, and the model, prompts and tool parser stay as they are.

[SpecStream for DR TuluAgent frameworks & builders](https://specstream.ai/integrations/dr-tulu.md)

## What you need

-   DR Tulu's `agent` library installed from its repository with `uv pip install -e .`, as the README shows, so the files you edit are the ones Python runs. A copy installed from PyPI sits in site-packages and these steps do not change it.
-   A SpecStream key. [Get your key](https://specstream.ai/workspace/search?route=serper&utm_source=dr-tulu&utm_medium=howto) on the Search API screen of your SpecStream workspace. New accounts start with $10 of credit, no card.

1.  1

    ## 1. Point the Serper search at SpecStream

    DR Tulu has no setting for its search address. `search_serper` in `agent/dr_agent/mcp_backend/apis/serper_apis.py` posts every search to `https://google.serper.dev/search`. From the `agent` directory, change that address to SpecStream's Serper route. On macOS, write `sed -i ''` in place of `sed -i`.

    Shell, in dr-tulu/agent

    ```bash
    sed -i 's#https://google.serper.dev/search#https://api.specstream.ai/serper/search#' \
      dr_agent/mcp_backend/apis/serper_apis.py
    ```

2.  2

    ## 2. Point the Jina Reader browsing at SpecStream

    `fetch_webpage_content_jina` in `agent/dr_agent/mcp_backend/apis/jina_apis.py` reads a page by calling `https://r.jina.ai/` followed by the page's address. Change the prefix to SpecStream's Jina Reader route. The page's address stays on the end, as before.

    Shell, in dr-tulu/agent

    ```bash
    sed -i 's#https://r.jina.ai/#https://api.specstream.ai/jina/#' \
      dr_agent/mcp_backend/apis/jina_apis.py
    
    grep -n "api.specstream.ai" dr_agent/mcp_backend/apis/*.py
    ```

3.  3

    ## 3. Put your SpecStream key in both variables

    In `agent/.env`, the file you copied from `.env.example`, set `SERPER_API_KEY` and `JINA_API_KEY` to your SpecStream key. DR Tulu sends the first in the `X-API-KEY` header and the second as a Bearer token, and those are the places SpecStream reads the key on each route. Keep `.env` out of anything you commit.

    Leave `S2_API_KEY` as it is. With the default `search_tool_name` of `serper`, both of the agent's search tools, `snippet_search` and `google_search`, go through Serper.

    agent/.env

    ```bash
    SERPER_API_KEY=YOUR_SPECSTREAM_KEY
    JINA_API_KEY=YOUR_SPECSTREAM_KEY
    ```

4.  4

    ## 4. Restart the MCP backend without its cache

    The MCP backend reads both files when it starts, so stop a backend that is running and start it again. DR Tulu keeps API replies on disk for 10 days, so a question you asked before would come back from that cache. Start the backend with `--no-cache` while you check the change.

    Shell, in dr-tulu/agent

    ```bash
    python -m dr_agent.mcp_backend.main --port 8000 --no-cache
    ```

5.  5

    ## 5. Ask DR Tulu

    Start the workflow as the README does. It finds the backend on port 8000 and uses it. Ask about something from this week, and the answer cites pages read through SpecStream. Each call is in your account's request log with its route as the endpoint.

    Shell, in dr-tulu/agent

    ```bash
    python workflows/auto_search_sft.py serve --port 8080 --config workflows/auto_search_sft.yaml
    
    # or a short evaluation run
    python workflows/auto_search_sft.py generate-dataset simpleqa \
      --num-examples 2 --config workflows/auto_search_sft.yaml --output results.jsonl
    ```

## What changes and what stays

Each SpecStream route answers in the shape DR Tulu already parses. The Serper route returns `organic[]` with `title`, `link`, `snippet` and `position`, and `SerperSearchTool` reads `title`, `link` and `snippet`. The Jina Reader route, asked with `Accept: application/json` as DR Tulu asks, returns `data.title`, `data.url` and `data.content`, and `JinaBrowseTool` passes `content` to the model. DR Tulu still sends `gl`, `hl` and `num`, and SpecStream reads them.

Keep `browse_tool_name: "jina"`, as the shipped `auto_search_sft.yaml` sets it. The `serper` browse tool calls `https://scrape.serper.dev`, and the Scholar tool calls `https://google.serper.dev/scholar`. The two `sed` lines leave both addresses as they were, and the default workflow calls neither.

## Evaluation runs

The repository's evaluation scripts run `workflows/auto_search_sft.py` with `search_tool_name=serper` and `browse_tool_name=jina` in `--config-overrides`. They reach the same two functions, so a benchmark run searches and reads through SpecStream with no other change. If several machines share one checkout, apply the two lines once in that checkout, and set the key in the environment each backend starts from.

## What each call costs

-   A search is one Search API call through a drop-in address: $1 per 1,000 searches of up to 10 results. The shipped config asks for 10 results (`number_documents_to_search: 10`), so each search stays at that price. A search that returns no results costs nothing.
-   A page read is one Extract API page at $0.80 per 1,000 pages. A page that gives no text is not charged.
-   The shipped config allows the agent 10 tool calls a question (`search_agent_max_tool_calls: 10`), so one question costs at most 10 searches or page reads.
-   Calls come out of your balance with no quote and no approval, and every call, charged or not, is in your request log.

## Why run DR Tulu on SpecStream

DR Tulu's README sends you to two services for two keys, one for search and one for reading pages. On SpecStream one key and one balance pay for both, and the request log shows every search and every page a run made, which helps when you compare runs.

SpecStream's search API scores 97.9 F-score on all 1,000 SimpleQA Verified questions and 64.0 item F1 on all 200 WideSearch tasks. Scores and method are on the [benchmarks page](https://specstream.ai/benchmarks.md).

## Limits and errors

-   When the account has used its calls for the minute, SpecStream answers `429` with `Retry-After`, and nothing is charged. DR Tulu does not retry: the tool fails with an error that starts with `API request failed with status 429`.
-   `API request failed with status 401` means the key is missing or wrong. Check `SERPER_API_KEY` and `JINA_API_KEY` in the environment the backend starts from.
-   An old answer after the change means the backend served it from its cache. Restart it with `--no-cache`.

## Go back

Run `git checkout -- dr_agent/mcp_backend/apis/serper_apis.py dr_agent/mcp_backend/apis/jina_apis.py` in the `agent` directory, put your Serper and Jina keys back in `.env`, and restart the backend. Questions: [support@specstream.ai](mailto:support@specstream.ai).

## More

[SpecStream for DR Tulu](https://specstream.ai/integrations/dr-tulu.md) covers what it does in DR Tulu on one page. Every other tool has its guide in [Connect your tools](https://specstream.ai/docs/integrations.md). You see the most a request can cost before it starts, and the matches are paid from your balance. [Billing](https://specstream.ai/docs/billing.md) has the details. For help, email [support@specstream.ai](mailto:support@specstream.ai).
