Skip to content
Open
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
70 changes: 56 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<!-- prettier-ignore -->
[![PyPI version](https://img.shields.io/pypi/v/tabstack.svg?label=pypi%20(stable))](https://pypi.org/project/tabstack/)

Give your model the web without giving up your privacy. Never trained on. Private by default. Built by Mozilla.

The Tabstack Python library provides convenient access to the Tabstack REST API from any Python 3.9+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
Expand All @@ -26,15 +28,22 @@ The full API of this library can be found in [api.md](api.md).

```python
import os
import sys
from tabstack import Tabstack

client = Tabstack(
api_key=os.environ.get("TABSTACK_API_KEY"), # This is the default and can be omitted
)

automate_event = client.agent.automate(
task="Find the top 3 trending repositories and extract their names, descriptions, and star counts",
stream = client.agent.research(
query="What are the latest developments in quantum computing?",
)

for event in stream:
if event.event == "complete":
print(event.data.report)
elif event.event == "error":
print(event.data.message, file=sys.stderr)
```

While you can provide an `api_key` keyword argument,
Expand All @@ -57,16 +66,22 @@ client = AsyncTabstack(


async def main() -> None:
automate_event = await client.agent.automate(
stream = await client.agent.automate(
task="Find the top 3 trending repositories and extract their names, descriptions, and star counts",
url="https://github.com/trending",
)
async for event in stream:
if event.event == "complete":
print(event.data.final_answer)


asyncio.run(main())
```

Functionality between the synchronous and asynchronous clients is otherwise identical.

`client.agent.automate()` and `client.agent.research()` always stream. They return a `Stream` (or `AsyncStream`) of `AutomateEvent` or `ResearchEvent`, and the final result arrives in the `complete` event. Both unions include an `error` event, and `AutomateEvent` also ends with a `done` event.

### With aiohttp

By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
Expand All @@ -92,9 +107,13 @@ async def main() -> None:
api_key=os.environ.get("TABSTACK_API_KEY"), # This is the default and can be omitted
http_client=DefaultAioHttpClient(),
) as client:
automate_event = await client.agent.automate(
stream = await client.agent.automate(
task="Find the top 3 trending repositories and extract their names, descriptions, and star counts",
url="https://github.com/trending",
)
async for event in stream:
if event.event == "complete":
print(event.data.final_answer)


asyncio.run(main())
Expand All @@ -118,11 +137,14 @@ from tabstack import Tabstack

client = Tabstack()

automate_event = client.agent.automate(
stream = client.agent.automate(
task="Find the top 3 trending repositories and extract their names, descriptions, and star counts",
geo_target={},
url="https://github.com/trending",
geo_target={"country": "US"},
)
print(automate_event.geo_target)
for event in stream:
if event.event == "complete":
print(event.data.final_answer)
```

## Handling errors
Expand All @@ -141,9 +163,18 @@ from tabstack import Tabstack
client = Tabstack()

try:
client.agent.automate(
stream = client.agent.automate(
task="Find the top 3 trending repositories and extract their names, descriptions, and star counts",
url="https://github.com/trending",
)
for event in stream:
if event.event == "complete":
if event.data.success:
print(event.data.final_answer)
elif event.data.error is not None:
print(event.data.error.message)
elif event.event == "error":
print(event.data.error.message)
except tabstack.APIConnectionError as e:
print("The server could not be reached")
print(e.__cause__) # an underlying Exception, likely raised within httpx.
Expand All @@ -155,6 +186,8 @@ except tabstack.APIStatusError as e:
print(e.response)
```

Errors that happen after the stream opens arrive as events in the stream (an `error` event, or a `complete` event with `success: false`), not as a raised `APIError`.

Error codes are as follows:

| Status Code | Error Type |
Expand Down Expand Up @@ -186,17 +219,21 @@ client = Tabstack(
)

# Or, configure per-request:
client.with_options(max_retries=5).agent.automate(
stream = client.with_options(max_retries=5).agent.automate(
task="Find the top 3 trending repositories and extract their names, descriptions, and star counts",
)
for event in stream:
if event.event == "complete":
print(event.data.final_answer)
```

### Timeouts

By default requests time out after 1 minute. You can configure this with a `timeout` option,
By default requests time out after 1 minute. `client.agent.automate()` and `client.agent.research()` default to 10 minutes unless you change the client-level `timeout`. You can configure this with a `timeout` option,
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:

```python
import httpx
from tabstack import Tabstack

# Configure the default for all requests:
Expand All @@ -211,9 +248,12 @@ client = Tabstack(
)

# Override per-request:
client.with_options(timeout=5.0).agent.automate(
stream = client.with_options(timeout=5.0).agent.automate(
task="Find the top 3 trending repositories and extract their names, descriptions, and star counts",
)
for event in stream:
if event.event == "complete":
print(event.data.final_answer)
```

On timeout, an `APITimeoutError` is thrown.
Expand Down Expand Up @@ -259,8 +299,10 @@ response = client.agent.with_raw_response.automate(
)
print(response.headers.get('X-My-Header'))

agent = response.parse() # get the object that `agent.automate()` would have returned
print(agent)
stream = response.parse() # get the object that `agent.automate()` would have returned
for event in stream:
if event.event == "complete":
print(event.data.final_answer)
```

These methods return an [`APIResponse`](https://github.com/Mozilla-Ocho/tabstack-python/tree/main/src/tabstack/_response.py) object.
Expand All @@ -269,7 +311,7 @@ The async client returns an [`AsyncAPIResponse`](https://github.com/Mozilla-Ocho

#### `.with_streaming_response`

The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
The above interface eagerly reads the full response body when you make the request, which may not always be what you want. Streaming methods are the exception: for `client.agent.automate()` and `client.agent.research()`, `parse()` returns the event `Stream`, which reads the body as you iterate it.

To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.

Expand Down
Loading