Skip to content

Commit 59778b9

Browse files
authored
Move from legacy proxy to gateway (#191)
The AI Proxy will be deprecated soon, and the AI Gateway is being continually updated with bug fixes and new features, and so we should move this repository to use that.
1 parent 9eba0fe commit 59778b9

6 files changed

Lines changed: 127 additions & 36 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,24 @@ import { Factuality } from "autoevals";
101101

102102
## Using other AI providers
103103

104-
When you use Autoevals, it will look for an `OPENAI_BASE_URL` environment variable to use as the base for requests to an OpenAI compatible API. If `OPENAI_BASE_URL` is not set, it will default to the [AI proxy](https://www.braintrust.dev/docs/guides/proxy).
104+
When you use Autoevals, it will look for an `OPENAI_BASE_URL` environment variable to use as the base for requests to an OpenAI-compatible API. If `OPENAI_BASE_URL` is not set, it will look for a `BRAINTRUST_AI_GATEWAY_URL` environment variable and then default to the [Braintrust Gateway](https://www.braintrust.dev/docs/deploy/gateway).
105105

106-
If you choose to use the proxy, you'll also get:
106+
When you use the Braintrust Gateway, you'll also get:
107107

108108
- Simplified access to many AI providers
109109
- Reduced costs with automatic request caching
110110
- Increased observability when you enable logging to Braintrust
111111

112-
The proxy is free to use, even if you don't have a Braintrust account.
112+
The Braintrust-hosted Gateway is free to use while it is in beta.
113113

114-
If you have a Braintrust account, you can optionally set the `BRAINTRUST_API_KEY` environment variable instead of `OPENAI_API_KEY` to unlock additional features like logging and monitoring. You can also route requests to [supported AI providers and models](https://www.braintrust.dev/docs/guides/proxy#supported-models) or custom models you have configured in Braintrust.
114+
Set the `BRAINTRUST_API_KEY` environment variable to authenticate Gateway requests. You can also route requests to supported AI providers and models or custom models you have configured in Braintrust.
115115

116116
<div className="tabs">
117117

118118
### Python
119119

120120
```python
121-
# NOTE: ensure BRAINTRUST_API_KEY is set in your environment and OPENAI_API_KEY is not set
121+
# NOTE: ensure BRAINTRUST_API_KEY is set in your environment
122122
from autoevals.llm import *
123123

124124
# Create an LLM-based evaluator using the Claude 3.5 Sonnet model from Anthropic
@@ -139,7 +139,7 @@ print(f"Factuality metadata: {result.metadata['rationale']}")
139139
### TypeScript
140140

141141
```typescript
142-
// NOTE: ensure BRAINTRUST_API_KEY is set in your environment and OPENAI_API_KEY is not set
142+
// NOTE: ensure BRAINTRUST_API_KEY is set in your environment
143143
import { Factuality } from "autoevals";
144144

145145
(async () => {

js/oai.test.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,23 @@ beforeAll(() => {
2626

2727
let OPENAI_API_KEY: string | undefined;
2828
let OPENAI_BASE_URL: string | undefined;
29+
let BRAINTRUST_API_KEY: string | undefined;
30+
let BRAINTRUST_AI_GATEWAY_URL: string | undefined;
2931

3032
beforeEach(() => {
3133
OPENAI_API_KEY = process.env.OPENAI_API_KEY;
3234
OPENAI_BASE_URL = process.env.OPENAI_BASE_URL;
35+
BRAINTRUST_API_KEY = process.env.BRAINTRUST_API_KEY;
36+
BRAINTRUST_AI_GATEWAY_URL = process.env.BRAINTRUST_AI_GATEWAY_URL;
3337
});
3438

3539
afterEach(() => {
3640
server.resetHandlers();
3741

3842
process.env.OPENAI_API_KEY = OPENAI_API_KEY;
3943
process.env.OPENAI_BASE_URL = OPENAI_BASE_URL;
44+
process.env.BRAINTRUST_API_KEY = BRAINTRUST_API_KEY;
45+
process.env.BRAINTRUST_AI_GATEWAY_URL = BRAINTRUST_AI_GATEWAY_URL;
4046

4147
// Reset init state
4248
init({ client: undefined, defaultModel: undefined });
@@ -120,13 +126,14 @@ describe("OAI", () => {
120126
);
121127
});
122128

123-
test("calls proxy if everything unset", async () => {
129+
test("calls gateway if everything unset", async () => {
124130
delete process.env.OPENAI_API_KEY;
125131
delete process.env.OPENAI_BASE_URL;
132+
delete process.env.BRAINTRUST_AI_GATEWAY_URL;
133+
process.env.BRAINTRUST_API_KEY = "braintrust-test-key";
126134

127135
server.use(
128-
http.post("https://api.braintrust.dev/v1/proxy/chat/completions", () => {
129-
debugger;
136+
http.post("https://gateway.braintrust.dev/chat/completions", () => {
130137
return HttpResponse.json(MOCK_OPENAI_COMPLETION_RESPONSE);
131138
}),
132139
);
@@ -137,7 +144,28 @@ describe("OAI", () => {
137144
messages: [{ role: "user", content: "Hello" }],
138145
});
139146

140-
debugger;
147+
expect(response.choices[0].message.content).toBe(
148+
"Hello, I am a mock response!",
149+
);
150+
});
151+
152+
test("uses configured Braintrust Gateway URL", async () => {
153+
delete process.env.OPENAI_API_KEY;
154+
delete process.env.OPENAI_BASE_URL;
155+
process.env.BRAINTRUST_API_KEY = "braintrust-test-key";
156+
process.env.BRAINTRUST_AI_GATEWAY_URL = " https://gateway.example.com ";
157+
158+
server.use(
159+
http.post("https://gateway.example.com/chat/completions", () => {
160+
return HttpResponse.json(MOCK_OPENAI_COMPLETION_RESPONSE);
161+
}),
162+
);
163+
164+
const client = buildOpenAIClient({});
165+
const response = await client.chat.completions.create({
166+
model: "gpt-4",
167+
messages: [{ role: "user", content: "Hello" }],
168+
});
141169

142170
expect(response.choices[0].message.content).toBe(
143171
"Hello, I am a mock response!",
@@ -147,9 +175,11 @@ describe("OAI", () => {
147175
test("default wraps", async () => {
148176
delete process.env.OPENAI_API_KEY;
149177
delete process.env.OPENAI_BASE_URL;
178+
delete process.env.BRAINTRUST_AI_GATEWAY_URL;
179+
process.env.BRAINTRUST_API_KEY = "braintrust-test-key";
150180

151181
server.use(
152-
http.post("https://api.braintrust.dev/v1/proxy/chat/completions", () => {
182+
http.post("https://gateway.braintrust.dev/chat/completions", () => {
153183
return HttpResponse.json(MOCK_OPENAI_COMPLETION_RESPONSE);
154184
}),
155185
);
@@ -173,9 +203,11 @@ describe("OAI", () => {
173203
test("wraps once", async () => {
174204
delete process.env.OPENAI_API_KEY;
175205
delete process.env.OPENAI_BASE_URL;
206+
delete process.env.BRAINTRUST_AI_GATEWAY_URL;
207+
process.env.BRAINTRUST_API_KEY = "braintrust-test-key";
176208

177209
server.use(
178-
http.post("https://api.braintrust.dev/v1/proxy/chat/completions", () => {
210+
http.post("https://gateway.braintrust.dev/chat/completions", () => {
179211
return HttpResponse.json(MOCK_OPENAI_COMPLETION_RESPONSE);
180212
}),
181213
);

js/oai.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,18 @@ export function extractOpenAIArgs<T extends Record<string, unknown>>(
8888
};
8989
}
9090

91-
const PROXY_URL = "https://api.braintrust.dev/v1/proxy";
91+
const DEFAULT_GATEWAY_URL = "https://gateway.braintrust.dev";
92+
93+
const getGatewayURL = (): string =>
94+
(process.env.BRAINTRUST_AI_GATEWAY_URL ?? "").trim() || DEFAULT_GATEWAY_URL;
95+
96+
const isGatewayBaseURL = (baseURL: string): boolean => {
97+
const normalizedBaseURL = baseURL.replace(/\/+$/, "");
98+
return (
99+
normalizedBaseURL === DEFAULT_GATEWAY_URL ||
100+
normalizedBaseURL === getGatewayURL().replace(/\/+$/, "")
101+
);
102+
};
92103

93104
const resolveOpenAIClient = (options: OpenAIAuth): OpenAI => {
94105
const {
@@ -121,13 +132,18 @@ const resolveOpenAIClient = (options: OpenAIAuth): OpenAI => {
121132
});
122133
}
123134

135+
const baseURL =
136+
openAiBaseUrl || process.env.OPENAI_BASE_URL || getGatewayURL();
137+
const apiKey =
138+
openAiApiKey ||
139+
(isGatewayBaseURL(baseURL)
140+
? process.env.BRAINTRUST_API_KEY || process.env.OPENAI_API_KEY
141+
: process.env.OPENAI_API_KEY || process.env.BRAINTRUST_API_KEY);
142+
124143
return new OpenAI({
125-
apiKey:
126-
openAiApiKey ||
127-
process.env.OPENAI_API_KEY ||
128-
process.env.BRAINTRUST_API_KEY,
144+
apiKey,
129145
organization: openAiOrganizationId,
130-
baseURL: openAiBaseUrl || process.env.OPENAI_BASE_URL || PROXY_URL,
146+
baseURL,
131147
defaultHeaders: openAiDefaultHeaders,
132148
dangerouslyAllowBrowser: openAiDangerouslyAllowBrowser,
133149
});
@@ -179,7 +195,7 @@ export interface InitOptions {
179195
/**
180196
* An OpenAI-compatible client to use for all evaluations.
181197
* This can be an OpenAI client, or any client that implements the OpenAI API
182-
* (e.g., configured to use the Braintrust proxy with Anthropic, Gemini, etc.)
198+
* (e.g., configured to use the Braintrust Gateway with Anthropic, Gemini, etc.)
183199
*/
184200
client?: OpenAI;
185201
/**
@@ -192,7 +208,7 @@ export interface InitOptions {
192208
* default models for different evaluation types. Only the specified models
193209
* are updated; others remain unchanged.
194210
*
195-
* When using non-OpenAI providers via the Braintrust proxy, set this to
211+
* When using non-OpenAI providers via the Braintrust Gateway, set this to
196212
* the appropriate model string (e.g., "claude-3-5-sonnet-20241022").
197213
*
198214
* @example
@@ -243,14 +259,14 @@ export interface InitOptions {
243259
* init({ client: new OpenAI() });
244260
*
245261
* @example
246-
* // Using with Anthropic via Braintrust proxy
262+
* // Using with Anthropic via Braintrust Gateway
247263
* import { init } from "autoevals";
248264
* import { OpenAI } from "openai";
249265
*
250266
* init({
251267
* client: new OpenAI({
252268
* apiKey: process.env.BRAINTRUST_API_KEY,
253-
* baseURL: "https://api.braintrust.dev/v1/proxy",
269+
* baseURL: process.env.BRAINTRUST_AI_GATEWAY_URL || "https://gateway.braintrust.dev",
254270
* }),
255271
* defaultModel: {
256272
* completion: "claude-3-5-sonnet-20241022",

py/autoevals/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
- Both sync and async evaluation support
1616
- Configurable scoring parameters
1717
- Detailed feedback through metadata
18-
- Integration with OpenAI and other LLM providers through Braintrust AI Proxy
18+
- Integration with OpenAI and other LLM providers through the Braintrust Gateway
1919
2020
**Client setup**:
2121
@@ -43,20 +43,20 @@
4343
evaluator = ClosedQA(client=client)
4444
```
4545
46-
**Multi-provider support via the Braintrust AI Proxy**:
46+
**Multi-provider support via the Braintrust Gateway**:
4747
48-
Autoevals supports multiple LLM providers (Anthropic, Azure, etc.) through the Braintrust AI Proxy.
49-
Configure your client to use the proxy and set the default model:
48+
Autoevals supports multiple LLM providers (Anthropic, Azure, etc.) through the Braintrust Gateway.
49+
Configure your client to use the Gateway and set the default model:
5050
5151
```python
5252
import os
5353
from openai import AsyncOpenAI
5454
from autoevals import init
5555
from autoevals.llm import Factuality
5656
57-
# Configure client to use Braintrust AI Proxy with Claude
57+
# Configure client to use the Braintrust Gateway with Claude
5858
client = AsyncOpenAI(
59-
base_url="https://api.braintrust.dev/v1/proxy",
59+
base_url=os.getenv("BRAINTRUST_AI_GATEWAY_URL") or "https://gateway.braintrust.dev",
6060
api_key=os.getenv("BRAINTRUST_API_KEY"),
6161
)
6262

py/autoevals/oai.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@
99
from dataclasses import dataclass
1010
from typing import Any, Optional, Protocol, TypedDict, TypeVar, Union, cast, runtime_checkable
1111

12-
PROXY_URL = "https://api.braintrust.dev/v1/proxy"
12+
GATEWAY_URL = "https://gateway.braintrust.dev"
13+
14+
15+
def _gateway_url() -> str:
16+
return os.environ.get("BRAINTRUST_AI_GATEWAY_URL", "").strip() or GATEWAY_URL
17+
18+
19+
def _is_gateway_url(base_url: str) -> bool:
20+
normalized_base_url = base_url.rstrip("/")
21+
return normalized_base_url == GATEWAY_URL or normalized_base_url == _gateway_url().rstrip("/")
1322

1423

1524
class DefaultModelConfig(TypedDict, total=False):
@@ -195,7 +204,9 @@ def __post_init__(self):
195204
if not has_customization and not isinstance(self.openai, NamedWrapper):
196205
self.openai = wrap_openai(self.openai)
197206

198-
self._is_wrapped = isinstance(self.openai, NamedWrapper)
207+
self._is_wrapped = isinstance(self.openai, NamedWrapper) or (
208+
not has_customization and wrap_openai.__module__.startswith("braintrust.")
209+
)
199210

200211
openai_module = get_openai_module()
201212

@@ -431,7 +442,7 @@ def init(
431442
models for different evaluation types. Only the specified models are updated;
432443
others remain unchanged.
433444
434-
When using non-OpenAI providers via the Braintrust proxy, set this to the
445+
When using non-OpenAI providers via the Braintrust Gateway, set this to the
435446
appropriate model string (e.g., "claude-3-5-sonnet-20241022").
436447
437448
Example:
@@ -448,7 +459,7 @@ def init(
448459
init(
449460
client=OpenAI(
450461
api_key=os.environ["BRAINTRUST_API_KEY"],
451-
base_url="https://api.braintrust.dev/v1/proxy",
462+
base_url=os.getenv("BRAINTRUST_AI_GATEWAY_URL") or "https://gateway.braintrust.dev",
452463
),
453464
default_model={
454465
"completion": "claude-3-5-sonnet-20241022",
@@ -520,7 +531,8 @@ def prepare_openai(
520531
Deprecated: Use the `client` argument and set the `openai`.
521532
522533
base_url (str, optional): Base URL for API requests. If not provided, will
523-
use OPENAI_BASE_URL from environment or fall back to PROXY_URL.
534+
use OPENAI_BASE_URL from environment or fall back to BRAINTRUST_AI_GATEWAY_URL
535+
or GATEWAY_URL.
524536
Deprecated: Use the `client` argument and set the `openai`.
525537
526538
Returns:
@@ -559,11 +571,14 @@ def prepare_openai(
559571
)
560572
warned_deprecated_api_key_base_url = True
561573

574+
if base_url is None:
575+
base_url = os.environ.get("OPENAI_BASE_URL") or _gateway_url()
562576
# prepare the default openai sdk, if not provided
563577
if api_key is None:
564-
api_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("BRAINTRUST_API_KEY")
565-
if base_url is None:
566-
base_url = os.environ.get("OPENAI_BASE_URL", PROXY_URL)
578+
if _is_gateway_url(base_url):
579+
api_key = os.environ.get("BRAINTRUST_API_KEY") or os.environ.get("OPENAI_API_KEY")
580+
else:
581+
api_key = os.environ.get("OPENAI_API_KEY") or os.environ.get("BRAINTRUST_API_KEY")
567582

568583
if hasattr(openai_module, "OpenAI"):
569584
openai_module = cast(OpenAIV1Module, openai_module)

py/autoevals/test_oai.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from autoevals import init # type: ignore[import]
99
from autoevals.oai import ( # type: ignore[import]
10+
GATEWAY_URL,
1011
LLMClient,
1112
OpenAIV0Module,
1213
OpenAIV1Module,
@@ -28,6 +29,7 @@ def reset_env_and_client(monkeypatch: pytest.MonkeyPatch):
2829
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
2930
monkeypatch.setenv("OPENAI_API_KEY", "test-key")
3031
monkeypatch.setenv("OPENAI_BASE_URL", "http://test-url")
32+
monkeypatch.delenv("BRAINTRUST_AI_GATEWAY_URL", raising=False)
3133
monkeypatch.setattr("autoevals.oai._named_wrapper", None)
3234
monkeypatch.setattr("autoevals.oai._wrap_openai", None)
3335
monkeypatch.setattr("autoevals.oai._openai_module", None)
@@ -89,6 +91,32 @@ def test_prepare_openai_defaults():
8991
assert openai_obj.base_url == "http://test-url"
9092

9193

94+
def test_prepare_openai_defaults_to_gateway(monkeypatch: pytest.MonkeyPatch):
95+
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
96+
monkeypatch.delenv("BRAINTRUST_AI_GATEWAY_URL", raising=False)
97+
monkeypatch.setenv("OPENAI_API_KEY", "openai-key")
98+
monkeypatch.setenv("BRAINTRUST_API_KEY", "braintrust-key")
99+
100+
prepared_client = prepare_openai()
101+
102+
openai_obj = unwrap_named_wrapper(prepared_client.openai)
103+
assert openai_obj.api_key == "braintrust-key"
104+
assert str(openai_obj.base_url).rstrip("/") == GATEWAY_URL
105+
106+
107+
def test_prepare_openai_uses_configured_gateway_url(monkeypatch: pytest.MonkeyPatch):
108+
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
109+
monkeypatch.setenv("BRAINTRUST_AI_GATEWAY_URL", " https://gateway.example.com ")
110+
monkeypatch.setenv("OPENAI_API_KEY", "openai-key")
111+
monkeypatch.setenv("BRAINTRUST_API_KEY", "braintrust-key")
112+
113+
prepared_client = prepare_openai()
114+
115+
openai_obj = unwrap_named_wrapper(prepared_client.openai)
116+
assert openai_obj.api_key == "braintrust-key"
117+
assert str(openai_obj.base_url).rstrip("/") == "https://gateway.example.com"
118+
119+
92120
def test_prepare_openai_with_plain_openai():
93121
client = openai.OpenAI(api_key="api-key", base_url="http://test")
94122
prepared_client = prepare_openai(client=client)

0 commit comments

Comments
 (0)