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
30 changes: 11 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@ name: Continuous Integration
on:
push:
branches:
- '**'
- main
pull_request:
branches:
- '**'

jobs:
unit-tests:
checks:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ['3.9', '3.10', '3.11']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']

steps:
- name: Checkout code
Expand All @@ -26,20 +24,14 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Install
run: pip install -e ".[dev]"

- name: Run type check
run: |
mypy --install-types deepinfra --non-interactive --verbose
- name: Lint
run: ruff check .

- name: Run lint check
run: |
black --check --verbose deepinfra
- name: Type check
run: mypy

- name: Run unit tests
run: |
pytest tests
- name: Unit tests
run: pytest tests
33 changes: 19 additions & 14 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,37 @@ name: Publish to PyPI
on:
push:
tags:
- '*'
- 'v*'

jobs:
build-n-publish:
name: Build and publish Python 🐍 distributions 📦 to PyPI
name: Build and publish to PyPI
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # PyPI trusted publishing (OIDC)

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version: '3.12'

- name: Install dependencies
- name: Check tag matches package version
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
pkg_version=$(grep -o '"[^"]*"' deepinfra/_version.py | tr -d '"')
tag_version="${GITHUB_REF_NAME#v}"
if [ "$pkg_version" != "$tag_version" ]; then
echo "Tag $GITHUB_REF_NAME does not match _version.py ($pkg_version)" >&2
exit 1
fi

- name: Build dist
run: |
python setup.py sdist bdist_wheel
python -m pip install --upgrade pip build
python -m build

- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@v1.4.2
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Changelog

## 0.2.0 (2026-07-10)

- New feature: **Sandboxes** — `Sandbox.create/from_id/list`, `exec`,
`run_python`, `fs.read/write`, `stop/start/terminate`, context managers, and
async twins (`acreate`, `aexec`, ...) for everything.
- New unified `DeepInfraClient` built on httpx (sync + async, pooled
connections, explicit timeouts, typed errors, retry policy: connect-error
retries where marked, 502/503/504 retries only on GETs).
- Typed exception hierarchy under `deepinfra` /`deepinfra.exceptions`;
`MaxRetriesExceededError` is now a subclass of `APIConnectionError`.
- The inference wrappers (`TextGeneration`, `Embeddings`,
`AutomaticSpeechRecognition`, `TextToImage`) keep their public API but now
run on the unified client; `requests`/`requests-toolbelt` dependencies
replaced by `httpx` + `pydantic`.
- `from deepinfra import TextGeneration` now works (it was missing from the
package exports in 0.1.0).
- Packaging modernized: pyproject (hatchling), `py.typed`, Python >= 3.9.

Breaking (low impact):
- `DeepInfraClient(url, token)` positional constructor replaced by
`DeepInfraClient(api_key=..., base_url=...)` with per-request paths.
- Network failures raise SDK exceptions instead of `requests` exceptions.

## 0.1.0 (2024-03-21)

- Initial release: inference API wrappers over `requests`.
109 changes: 85 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,112 @@
[![Python Version](https://img.shields.io/pypi/pyversions/deepinfra.svg)](https://pypi.org/project/deepinfra/)
[![License](https://img.shields.io/github/license/deepinfra/deepinfra-python.svg)](LICENSE)

`deepinfra` is a Python library designed to provide a simple interface for interacting with DeepInfra's Inference API, facilitating various AI and machine learning tasks.
The official Python SDK for the [DeepInfra](https://deepinfra.com) API:
**Sandboxes** (isolated microVMs for running untrusted code) and inference.

## Installation

To install `deepinfra`, run the following command:

```bash
pip install deepinfra
```

## Examples
Authentication uses your DeepInfra API key — pass `api_key=` or set the
`DEEPINFRA_API_KEY` environment variable (the same key you use for inference,
from [deepinfra.com/dash/api_keys](https://deepinfra.com/dash/api_keys)).

### Use Automatic Speech Recognition
## Sandboxes

You can use the Automatic Speech Recognition (ASR) API to transcribe audio files, URLs and buffer objects.
#### Transcribe an audio file
Create an isolated Linux microVM, run bash/python inside it, move files in and
out, and tear it down — in a few lines:

```python
from deepinfra import AutomaticSpeechRecognition
from deepinfra import Sandbox

model_name = "openai/whisper-base"
asr = AutomaticSpeechRecognition(model_name)
sb = Sandbox.create(plan="medium", timeout="10m") # blocks until running

file_path = "path/to/audio/file"
body = {
"audio": file_path
}
transcription = asr.generate(body)
print(transcription["text"])
r = sb.exec("bash", "-c", "pip install pandas && python -c 'import pandas; print(pandas.__version__)'")
print(r.stdout, r.stderr, r.returncode)

out = sb.run_python("print(21 * 2)").check() # .check() raises on non-zero exit
print(out.stdout) # "42"

sb.fs.write("/work/in.csv", b"a,b\n1,2\n")
data = sb.fs.read("/work/in.csv")

sb.stop() # frees compute, keeps disk
sb.start() # resumes on the same disk
sb.terminate() # deletes the sandbox
```

Every network method has an async twin prefixed with `a`:

```python
sb = await Sandbox.acreate(plan="small")
r = await sb.aexec("uname", "-a")
await sb.aterminate()
```

Useful patterns:

```python
# Auto-terminate with a context manager
with Sandbox.create(plan="small") as sb:
sb.run_python("open('/work/out.txt', 'w').write('hi')")
print(sb.fs.read("/work/out.txt"))

# Find existing sandboxes
sb = Sandbox.from_id("sb_...")
etl_boxes = Sandbox.list(tags={"job": "etl-42"})

# Large scripts: upload, then run
sb.fs.write("/work/script.py", open("script.py").read())
sb.exec("python3", "/work/script.py", timeout="30m")
```

#### Transcribe an audio URL
Errors are typed: `AuthenticationError` (401), `NotFoundError` (404),
`ConflictError` (409, e.g. exec on a stopped sandbox), `TooManySandboxesError`
(429, per-account cap), `CapacityError` (503), plus SDK-side
`SandboxTimeoutError` / `SandboxFailedError` / `CommandFailedError`.

Roadmap (API designed, lands in an upcoming release): `exec_stream` (live
output), `snapshot()` / `Sandbox.from_snapshot()`, `expose_port()`,
`fs.upload_dir()`.

## Inference

The inference wrappers predate the SDK's OpenAI-compatible endpoints and remain
supported:

### Automatic Speech Recognition

```python
from deepinfra import AutomaticSpeechRecognition

model_name = "openai/whisper-base"
asr = AutomaticSpeechRecognition(model_name)
asr = AutomaticSpeechRecognition("openai/whisper-base")

url = "https://path/to/audio/file"
body = {
"audio": url
}
body = {"audio": "path/to/audio/file"} # or a URL, or raw bytes
transcription = asr.generate(body)
print(transcription["text"])
print(transcription.text)
```

### Text Generation

```python
from deepinfra import TextGeneration

llm = TextGeneration("mistralai/Mixtral-8x22B-Instruct-v0.1")
res = llm.generate({"input": "What is the capital of France?"})
print(res.results[0].generated_text)
```

`Embeddings` and `TextToImage` work the same way. For chat-style LLM usage you
can also point the official OpenAI client at
`https://api.deepinfra.com/v1/openai`.

## Development

```bash
pip install -e ".[dev]"
pytest tests # unit tests (no network)
mypy && ruff check . # types + lint
```
71 changes: 70 additions & 1 deletion deepinfra/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,70 @@
from .models import *
from ._exceptions import (
APIConnectionError,
APIStatusError,
APITimeoutError,
AuthenticationError,
BadRequestError,
CapacityError,
CommandFailedError,
ConflictError,
ContentTooLargeError,
DeepInfraError,
InternalServerError,
MaxRetriesExceededError,
NotFoundError,
PermissionDeniedError,
SandboxError,
SandboxExecError,
SandboxFailedError,
SandboxTimeoutError,
TooManySandboxesError,
)
from ._sandbox_models import ExecResult, SandboxInfo
from ._version import __version__
from .clients import DeepInfraClient, RequestSpec
from .models import (
AutomaticSpeechRecognition,
BaseModel,
Embeddings,
TextGeneration,
TextToImage,
)
from .sandbox_api import Sandbox, SandboxFS

__all__ = [
"__version__",
# sandboxes
"Sandbox",
"SandboxFS",
"SandboxInfo",
"ExecResult",
# client
"DeepInfraClient",
"RequestSpec",
# legacy inference wrappers (re-exported by .models)
"BaseModel",
"TextGeneration",
"TextToImage",
"Embeddings",
"AutomaticSpeechRecognition",
# exceptions
"DeepInfraError",
"APIConnectionError",
"APITimeoutError",
"APIStatusError",
"AuthenticationError",
"BadRequestError",
"PermissionDeniedError",
"NotFoundError",
"ConflictError",
"ContentTooLargeError",
"TooManySandboxesError",
"CapacityError",
"InternalServerError",
"MaxRetriesExceededError",
"SandboxError",
"SandboxTimeoutError",
"SandboxFailedError",
"SandboxExecError",
"CommandFailedError",
]
Loading
Loading