Skip to content

Commit f160f08

Browse files
Keep integration attribution on connection config (#1459)
moves integration attirbution to more private thing to avoid confusing people with first class kwargs
1 parent 4253883 commit f160f08

9 files changed

Lines changed: 48 additions & 27 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"e2b": patch
3+
"@e2b/python-sdk": patch
4+
---
5+
6+
Keep integration user-agent attribution on `ConnectionConfig` instead of exposing it through sandbox, template, volume, or per-call API option types.

packages/js-sdk/src/connectionConfig.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,6 @@ export interface ConnectionOpts {
9393
*/
9494
apiHeaders?: Record<string, string>
9595

96-
/**
97-
* Integration wrapping the E2B SDK, appended to the `User-Agent`.
98-
*
99-
* @example 'e2b-code-interpreter/0.1.0'
100-
*/
101-
integration?: string
102-
10396
/**
10497
* An optional `AbortSignal` that can be used to cancel the in-flight request.
10598
* When the signal is aborted, the underlying `fetch` is aborted and the
@@ -108,6 +101,18 @@ export interface ConnectionOpts {
108101
signal?: AbortSignal
109102
}
110103

104+
/**
105+
* Options accepted by `ConnectionConfig`.
106+
*/
107+
export interface ConnectionConfigOpts extends ConnectionOpts {
108+
/**
109+
* Integration wrapping the E2B SDK, appended to the `User-Agent`.
110+
*
111+
* @example 'e2b-code-interpreter/0.1.0'
112+
*/
113+
integration?: string
114+
}
115+
111116
/**
112117
* Build an `AbortSignal` that combines an optional request-timeout signal
113118
* (via `AbortSignal.timeout`) with an optional user-provided signal.
@@ -350,7 +355,7 @@ export class ConnectionConfig {
350355

351356
readonly proxy?: string
352357

353-
constructor(opts?: ConnectionOpts) {
358+
constructor(opts?: ConnectionConfigOpts) {
354359
this.apiKey = opts?.apiKey || ConnectionConfig.apiKey
355360
this.validateApiKey =
356361
opts?.validateApiKey ?? ConnectionConfig.validateApiKey

packages/js-sdk/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ export { ApiClient } from './api'
22
export type { components, paths } from './api'
33

44
export { ConnectionConfig } from './connectionConfig'
5-
export type { ConnectionOpts, Username } from './connectionConfig'
5+
export type {
6+
ConnectionConfigOpts,
7+
ConnectionOpts,
8+
Username,
9+
} from './connectionConfig'
610
export {
711
AuthenticationError,
812
FileNotFoundError,

packages/js-sdk/tests/connectionConfig.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ test('debug defaults to E2B_DEBUG env var', () => {
183183
assert.equal(config.debug, true)
184184
})
185185

186-
test('integration options are appended to the user agent', () => {
186+
test('integration option is appended to the user agent', () => {
187187
const config = new ConnectionConfig({
188188
integration: 'testing/version',
189189
})

packages/js-sdk/tests/sandbox/rpcHeaders.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ vi.mock('../../src/envd/http2', () => ({
1818
}))
1919

2020
test('does not pass custom connection headers to envd RPC requests', async () => {
21-
const { Sandbox } = await import('../../src')
21+
const { ConnectionConfig, Sandbox } = await import('../../src')
22+
const config = new ConnectionConfig({ integration: 'testing/version' })
2223
const sandbox = new Sandbox({
24+
...config,
2325
sandboxId: 'sbx-test',
2426
sandboxDomain: 'sandbox.e2b.dev',
2527
envdVersion: '0.2.4',
2628
envdAccessToken: 'tok',
27-
integration: 'testing/version',
2829
headers: {
2930
Authorization: 'Bearer user-token',
3031
'X-Custom': 'secret',

packages/python-sdk/e2b/connection_config.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ class ApiParams(TypedDict, total=False):
3131
api_headers: Optional[Dict[str, str]]
3232
"""Additional headers to send with E2B API requests."""
3333

34-
integration: Optional[str]
35-
"""Integration wrapping the E2B SDK, appended to the User-Agent."""
36-
3734
api_key: Optional[str]
3835
"""E2B API Key to use for authentication, defaults to `E2B_API_KEY` environment variable."""
3936

@@ -148,9 +145,10 @@ def __init__(
148145
self.access_token = access_token or ConnectionConfig._access_token()
149146
self.integration = integration
150147
self.headers = {**(headers or {}), **(api_headers or {})}
151-
self.headers["User-Agent"] = self._build_user_agent(
152-
self.integration,
153-
)
148+
if self.integration is not None or "User-Agent" not in self.headers:
149+
self.headers["User-Agent"] = self._build_user_agent(
150+
self.integration,
151+
)
154152
self.__extra_sandbox_headers = extra_sandbox_headers or {}
155153

156154
self.proxy = proxy
@@ -240,7 +238,6 @@ def get_api_params(
240238
"""
241239
headers = opts.get("headers")
242240
api_headers = opts.get("api_headers")
243-
integration = opts.get("integration", self.integration)
244241
request_timeout = opts.get("request_timeout")
245242
api_key = opts.get("api_key")
246243
validate_api_key = opts.get("validate_api_key")
@@ -255,9 +252,9 @@ def get_api_params(
255252
req_headers.update(headers)
256253
if api_headers is not None:
257254
req_headers.update(api_headers)
258-
if integration is not None:
255+
if self.integration is not None:
259256
req_headers["User-Agent"] = self._build_user_agent(
260-
integration,
257+
self.integration,
261258
)
262259

263260
# `logger` is a construction-time option rather than a per-request
@@ -279,7 +276,6 @@ def get_api_params(
279276
debug=debug if debug is not None else self.debug,
280277
request_timeout=self.get_request_timeout(request_timeout),
281278
headers=req_headers,
282-
integration=integration,
283279
proxy=proxy if proxy is not None else self.proxy,
284280
sandbox_url=(
285281
sandbox_url

packages/python-sdk/tests/async/sandbox_async/test_config_propagation.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,15 @@ async def test_connect_sets_stable_host_routing_headers(monkeypatch, test_api_ke
103103
)
104104
monkeypatch.setattr(sandbox_async_main.SandboxApi, "_cls_connect", mock_connect)
105105

106-
sandbox = await AsyncSandbox.connect(
107-
"sbx-test",
106+
config = ConnectionConfig(
108107
api_key=test_api_key,
109108
headers=BASE_HEADERS,
110109
integration="testing/version",
111110
)
111+
sandbox = await AsyncSandbox.connect(
112+
"sbx-test",
113+
**config.get_api_params(),
114+
)
112115

113116
assert sandbox.envd_api_url == "https://sandbox.e2b.app"
114117
assert "X-Test" not in sandbox.connection_config.sandbox_headers

packages/python-sdk/tests/sync/sandbox_sync/test_config_propagation.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,15 @@ def test_connect_sets_stable_host_routing_headers(monkeypatch, test_api_key):
9090
)
9191
monkeypatch.setattr(sandbox_sync_main.SandboxApi, "_cls_connect", mock_connect)
9292

93-
sandbox = Sandbox.connect(
94-
"sbx-test",
93+
config = ConnectionConfig(
9594
api_key=test_api_key,
9695
headers=BASE_HEADERS,
9796
integration="testing/version",
9897
)
98+
sandbox = Sandbox.connect(
99+
"sbx-test",
100+
**config.get_api_params(),
101+
)
99102

100103
assert sandbox.envd_api_url == "https://sandbox.e2b.app"
101104
assert "X-Test" not in sandbox.connection_config.sandbox_headers

packages/python-sdk/tests/test_connection_config.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def test_debug_defaults_to_env_var(monkeypatch):
131131
assert config.debug is True
132132

133133

134-
def test_integration_options_are_appended_to_user_agent():
134+
def test_integration_option_is_appended_to_user_agent():
135135
config = ConnectionConfig(integration="testing/version")
136136

137137
assert config.headers["User-Agent"].startswith("e2b-python-sdk/")
@@ -143,6 +143,9 @@ def test_integration_option_survives_api_param_rebuilds():
143143
rebuilt_config = ConnectionConfig(**config.get_api_params())
144144

145145
assert rebuilt_config.headers["User-Agent"].endswith(" testing/version")
146+
assert rebuilt_config.get_api_params(api_headers={"X-Test": "1"})["headers"][
147+
"User-Agent"
148+
].endswith(" testing/version")
146149

147150

148151
def test_request_timeout_zero_means_no_timeout():

0 commit comments

Comments
 (0)