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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ print(response.choices[0].message.content)
- **Embeddings**: Text embedding generation
- **Vision/Image**: Analyze images with multimodal models
- **Audio I/O**: Audio input and output support
- **Audio API**: Text to speech, transcription and translation (`client.audio`)
- **Video**: Video content analysis
- **Web Search**: Real-time web search integration
- **Context Compression**: Compress long conversation history to save tokens
Expand Down Expand Up @@ -154,6 +155,31 @@ response = client.chat.completions.create(
print(response.choices[0].message.audio.transcript)
```

### Speech, Transcription and Translation

```python
# Text to speech
speech = client.audio.speech.create(
model="gpt-4o-mini-tts",
input="Hello from Apertis!",
voice="alloy",
)
speech.write_to_file("hello.mp3")

# Speech to text: pass a path, an open file, bytes, or (filename, bytes)
transcript = client.audio.transcriptions.create(file="meeting.mp3", model="whisper-1")
print(transcript.text)

# Subtitles come back as plain text
srt = client.audio.transcriptions.create(
file="meeting.mp3", model="whisper-1", response_format="srt"
)

# Translate speech into English text
translation = client.audio.translations.create(file="bonjour.m4a", model="whisper-1")
print(translation.text)
```

### Web Search

Enable real-time web search for a non-free model by appending `:web` to its model ID.
Expand Down Expand Up @@ -581,6 +607,8 @@ the `publish` confirmation value.
## Changelog

### Unreleased
- Add `client.audio`: `speech.create()` (binary audio with `write_to_file()`),
`transcriptions.create()` and `translations.create()` (multipart uploads).
- Add `stream`, `thinking` and `extra_body` to `messages.create()`, with typed stream
events and `thinking` / `redacted_thinking` content blocks.
- Streamed requests that fail with an HTTP error now raise `APIError` (previously
Expand Down
28 changes: 26 additions & 2 deletions apertis/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,20 @@ def request(
json: Optional[Dict[str, Any]] = None,
headers: Optional[Mapping[str, str]] = None,
stream: bool = False,
data: Optional[Dict[str, Any]] = None,
files: Optional[Dict[str, Any]] = None,
) -> httpx.Response:
"""Make an HTTP request."""
"""Make an HTTP request.

With `files`, the body is multipart/form-data built from `data` and `files`;
file contents must already be bytes so a retry resends them unchanged.
"""
url = self._build_url(path)
request_headers = self._build_headers(headers)
if files is not None:
# httpx sets the multipart content type with its boundary.
for key in [k for k in request_headers if k.lower() == "content-type"]:
del request_headers[key]

retries = 0
last_exception: Optional[Exception] = None
Expand All @@ -85,6 +95,8 @@ def request(
method,
url,
json=json,
data=data,
files=files,
headers=request_headers,
extensions={"stream": stream} if stream else None,
)
Expand Down Expand Up @@ -196,10 +208,20 @@ async def request(
json: Optional[Dict[str, Any]] = None,
headers: Optional[Mapping[str, str]] = None,
stream: bool = False,
data: Optional[Dict[str, Any]] = None,
files: Optional[Dict[str, Any]] = None,
) -> httpx.Response:
"""Make an HTTP request."""
"""Make an HTTP request.

With `files`, the body is multipart/form-data built from `data` and `files`;
file contents must already be bytes so a retry resends them unchanged.
"""
url = self._build_url(path)
request_headers = self._build_headers(headers)
if files is not None:
# httpx sets the multipart content type with its boundary.
for key in [k for k in request_headers if k.lower() == "content-type"]:
del request_headers[key]

retries = 0
last_exception: Optional[Exception] = None
Expand All @@ -210,6 +232,8 @@ async def request(
method,
url,
json=json,
data=data,
files=files,
headers=request_headers,
extensions={"stream": stream} if stream else None,
)
Expand Down
3 changes: 3 additions & 0 deletions apertis/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Mapping

from apertis._base_client import AsyncClient, SyncClient
from apertis.resources.audio import AsyncAudio, Audio
from apertis.resources.chat import AsyncChat, Chat
from apertis.resources.embeddings import AsyncEmbeddings, Embeddings
from apertis.resources.models import AsyncModels, Models
Expand Down Expand Up @@ -58,6 +59,7 @@ def __init__(
self.responses = Responses(self._client)
self.messages = Messages(self._client)
self.rerank = Rerank(self._client)
self.audio = Audio(self._client)

def close(self) -> None:
"""Close the underlying HTTP client."""
Expand Down Expand Up @@ -115,6 +117,7 @@ def __init__(
self.responses = AsyncResponses(self._client)
self.messages = AsyncMessages(self._client)
self.rerank = AsyncRerank(self._client)
self.audio = AsyncAudio(self._client)

async def close(self) -> None:
"""Close the underlying HTTP client."""
Expand Down
Loading
Loading