Skip to content

Commit 8c05e64

Browse files
committed
feat: add api_token parameter support for legacy compatibility
Adds support for the legacy api_token parameter in both Replicate and AsyncReplicate client initialization as an alternative to bearer_token. This enables backward compatibility with v1.x client code that uses: - Client(api_token="...") - AsyncClient(api_token="...") The implementation: - Accepts both api_token and bearer_token parameters - Raises clear error if both are provided - Maps api_token to bearer_token internally - Maintains existing environment variable behavior - Includes comprehensive test coverage
1 parent a4878ab commit 8c05e64

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

src/replicate/_client.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def __init__(
102102
self,
103103
*,
104104
bearer_token: str | None = None,
105+
api_token: str | None = None, # Legacy compatibility parameter
105106
base_url: str | httpx.URL | None = None,
106107
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
107108
max_retries: int = DEFAULT_MAX_RETRIES,
@@ -124,7 +125,17 @@ def __init__(
124125
"""Construct a new synchronous Replicate client instance.
125126
126127
This automatically infers the `bearer_token` argument from the `REPLICATE_API_TOKEN` environment variable if it is not provided.
128+
129+
For legacy compatibility, you can also pass `api_token` instead of `bearer_token`.
127130
"""
131+
# Handle legacy api_token parameter
132+
if api_token is not None and bearer_token is not None:
133+
raise ReplicateError(
134+
"Cannot specify both 'bearer_token' and 'api_token'. Please use 'bearer_token' (recommended) or 'api_token' for legacy compatibility."
135+
)
136+
if api_token is not None:
137+
bearer_token = api_token
138+
128139
if bearer_token is None:
129140
bearer_token = _get_api_token_from_environment()
130141
if bearer_token is None:
@@ -477,6 +488,7 @@ def __init__(
477488
self,
478489
*,
479490
bearer_token: str | None = None,
491+
api_token: str | None = None, # Legacy compatibility parameter
480492
base_url: str | httpx.URL | None = None,
481493
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
482494
max_retries: int = DEFAULT_MAX_RETRIES,
@@ -499,7 +511,17 @@ def __init__(
499511
"""Construct a new async AsyncReplicate client instance.
500512
501513
This automatically infers the `bearer_token` argument from the `REPLICATE_API_TOKEN` environment variable if it is not provided.
514+
515+
For legacy compatibility, you can also pass `api_token` instead of `bearer_token`.
502516
"""
517+
# Handle legacy api_token parameter
518+
if api_token is not None and bearer_token is not None:
519+
raise ReplicateError(
520+
"Cannot specify both 'bearer_token' and 'api_token'. Please use 'bearer_token' (recommended) or 'api_token' for legacy compatibility."
521+
)
522+
if api_token is not None:
523+
bearer_token = api_token
524+
503525
if bearer_token is None:
504526
bearer_token = _get_api_token_from_environment()
505527
if bearer_token is None:
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""Tests for api_token legacy compatibility during client instantiation."""
2+
3+
from __future__ import annotations
4+
5+
import os
6+
import pytest
7+
8+
from replicate import Replicate, AsyncReplicate, ReplicateError
9+
from replicate._client import Client
10+
11+
12+
class TestApiTokenCompatibility:
13+
"""Test that api_token parameter works as a legacy compatibility option."""
14+
15+
def test_sync_client_with_api_token(self) -> None:
16+
"""Test that Replicate accepts api_token parameter."""
17+
client = Replicate(api_token="test_token_123")
18+
assert client.bearer_token == "test_token_123"
19+
20+
def test_async_client_with_api_token(self) -> None:
21+
"""Test that AsyncReplicate accepts api_token parameter."""
22+
client = AsyncReplicate(api_token="test_token_123")
23+
assert client.bearer_token == "test_token_123"
24+
25+
def test_sync_client_with_bearer_token(self) -> None:
26+
"""Test that Replicate still accepts bearer_token parameter."""
27+
client = Replicate(bearer_token="test_token_123")
28+
assert client.bearer_token == "test_token_123"
29+
30+
def test_async_client_with_bearer_token(self) -> None:
31+
"""Test that AsyncReplicate still accepts bearer_token parameter."""
32+
client = AsyncReplicate(bearer_token="test_token_123")
33+
assert client.bearer_token == "test_token_123"
34+
35+
def test_sync_client_both_tokens_error(self) -> None:
36+
"""Test that providing both api_token and bearer_token raises an error."""
37+
with pytest.raises(ReplicateError, match="Cannot specify both 'bearer_token' and 'api_token'"):
38+
Replicate(api_token="test_api", bearer_token="test_bearer")
39+
40+
def test_async_client_both_tokens_error(self) -> None:
41+
"""Test that providing both api_token and bearer_token raises an error."""
42+
with pytest.raises(ReplicateError, match="Cannot specify both 'bearer_token' and 'api_token'"):
43+
AsyncReplicate(api_token="test_api", bearer_token="test_bearer")
44+
45+
def test_sync_client_no_token_with_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
46+
"""Test that client reads from environment when no token is provided."""
47+
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token_123")
48+
client = Replicate()
49+
assert client.bearer_token == "env_token_123"
50+
51+
def test_async_client_no_token_with_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
52+
"""Test that async client reads from environment when no token is provided."""
53+
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token_123")
54+
client = AsyncReplicate()
55+
assert client.bearer_token == "env_token_123"
56+
57+
def test_sync_client_no_token_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
58+
"""Test that client raises error when no token is provided and env is not set."""
59+
monkeypatch.delenv("REPLICATE_API_TOKEN", raising=False)
60+
with pytest.raises(ReplicateError, match="The bearer_token client option must be set"):
61+
Replicate()
62+
63+
def test_async_client_no_token_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
64+
"""Test that async client raises error when no token is provided and env is not set."""
65+
monkeypatch.delenv("REPLICATE_API_TOKEN", raising=False)
66+
with pytest.raises(ReplicateError, match="The bearer_token client option must be set"):
67+
AsyncReplicate()
68+
69+
def test_legacy_client_alias(self) -> None:
70+
"""Test that legacy Client import still works as an alias."""
71+
assert Client is Replicate
72+
73+
def test_legacy_client_with_api_token(self) -> None:
74+
"""Test that legacy Client alias works with api_token parameter."""
75+
client = Client(api_token="test_token_123")
76+
assert client.bearer_token == "test_token_123"
77+
assert isinstance(client, Replicate)
78+
79+
def test_api_token_overrides_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
80+
"""Test that explicit api_token overrides environment variable."""
81+
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token")
82+
client = Replicate(api_token="explicit_token")
83+
assert client.bearer_token == "explicit_token"
84+
85+
def test_bearer_token_overrides_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
86+
"""Test that explicit bearer_token overrides environment variable."""
87+
monkeypatch.setenv("REPLICATE_API_TOKEN", "env_token")
88+
client = Replicate(bearer_token="explicit_token")
89+
assert client.bearer_token == "explicit_token"

0 commit comments

Comments
 (0)