feat(sdk): add an E2B client to the Code Interpreter SDKs - #1783
feat(sdk): add an E2B client to the Code Interpreter SDKs#1783devin-ai-integration[bot] wants to merge 3 commits into
Conversation
Move the configuration binding onto the resource classes in the core, so the core and the downstream clients share one implementation. Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
🦋 Changeset detectedLatest commit: a3bce7a The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Package ArtifactsBuilt from f17c887. Download artifacts from this workflow run. JS SDK ( npm install ./e2b-2.46.2-devin-1787774000-code-interpreter-e2b-client.0.tgzCLI ( npm install ./e2b-cli-2.18.1-devin-1787774000-code-interpreter-e2b-client.0.tgzCode Interpreter JS SDK ( npm install ./e2b-code-interpreter-2.7.3-devin-1787774000-code-interpreter-e2b-client.0.tgzDesktop JS SDK ( npm install ./e2b-desktop-2.3.4-devin-1787774000-code-interpreter-e2b-client.0.tgzPython SDK ( pip install ./e2b-2.46.1+devin.1787774000.code.interpreter.e2b.client-py3-none-any.whlCode Interpreter Python SDK ( pip install ./e2b_code_interpreter-2.9.2+devin.1787774000.code.interpreter.e2b.client-py3-none-any.whlDesktop Python SDK ( pip install ./e2b_desktop-2.4.5+devin.1787774000.code.interpreter.e2b.client-py3-none-any.whl |
There was a problem hiding this comment.
TASTE.md review (sdk-harness) of the new E2B client plumbing.
Checked: cross-language parity (T-1, T-2), API shape and option-type conventions (T-3, T-19, T-22, T-23), module/entry-point structure (T-54), configuration precedence and immutability (T-49–T-51), naming (T-9, T-12).
The core design holds up well: required-vs-optional shape is right (new E2B(opts?) / E2B(**opts), T-3), per-call options taking precedence keeps the explicit → env → default chain intact (T-49), the bound options are snapshotted rather than aliased (T-51), and hoisting the binding into ClientFactory so both SDKs and both downstream packages share one implementation is exactly the consistency T-1/T-2 ask for.
4 violations, all in the wiring rather than the design:
- 2× T-54 (entry-point/export hygiene)
- 1× T-23 (inline option type in a signature)
- 1× T-1a/T-22 (
withOptsvs_with_paramsnaming parity)
Not tied to a changed line: E2BClientParams in packages/code-interpreter-python/e2b_code_interpreter/client.py (and its e2b counterpart it mirrors) breaks T-22, which asks for the Opts suffix in both languages — E2BClientOpts in JS, so E2BClientOpts in Python too. Since the name is already shipped in the core e2b package, I'm not asking for it here alone; renaming both together in one change is the fix, and doing it now while the downstream client is still unreleased is cheaper than later. Same root cause as the _with_params comment below.
| @@ -1,5 +1,6 @@ | |||
| export * from 'e2b' | |||
|
|
|||
| export { E2B, type E2BClientOpts } from './client' | |||
There was a problem hiding this comment.
T-54 — one flat entry point, and in JS "runtime values use export and type-only names use export type — never mixed". This line mixes the class and the type alias in one statement; every other export in this file (and in js-sdk/src/index.ts) keeps them apart.
| export { E2B, type E2BClientOpts } from './client' | |
| export { E2B } from './client' | |
| export type { E2BClientOpts } from './client' |
Note the same mixed line already exists at packages/js-sdk/src/index.ts:160 from the earlier client PR — worth fixing there in this stack too rather than propagating it.
| Template, | ||
| Volume, | ||
| ) | ||
| from e2b.connection_config import ApiParams |
There was a problem hiding this comment.
T-54 — "one flat entry point per package … no subpath exports". Reaching into e2b.connection_config from a different package pins this client to the core's internal module layout, and it isn't necessary: ApiParams is already in e2b.__all__ (packages/python-sdk/e2b/__init__.py). The JS sibling gets this right (import { ConnectionOpts, … } from 'e2b').
| from e2b.connection_config import ApiParams | |
| from e2b import ApiParams |
(Or fold ApiParams into the from e2b import (...) block above and drop this line.)
| * @hidden | ||
| * @hide | ||
| */ | ||
| static withOpts<T>(this: T, opts?: Omit<ConnectionOpts, 'signal'>): T { |
There was a problem hiding this comment.
T-23 — "the option type is always named and exported from the entry point, never an inline intersection in the signature". Omit<ConnectionOpts, 'signal'> is spelled inline here, again in TemplateBase.withOpts (packages/js-sdk/src/template/index.ts), and a third time in packages/code-interpreter-js/src/client.ts — while the identical shape already has a name, E2BClientOpts. Three inline copies mean a future change to what is bindable has to be found by grep instead of following the type.
Declare the alias here in connectionConfig.ts (client.ts imports from this module, so it can't live there without a cycle) and re-export it unchanged from client.ts / index.ts, then:
| static withOpts<T>(this: T, opts?: Omit<ConnectionOpts, 'signal'>): T { | |
| static withOpts<T>(this: T, opts?: E2BClientOpts): T { |
| """ | ||
|
|
||
| @classmethod | ||
| def _with_params(cls, **api_params: Unpack[ApiParams]) -> Type[Self]: |
There was a problem hiding this comment.
T-1a / T-22 — the three surfaces "mirror each other 1:1 in names and semantics, differing only by language idiom", and option-shaped things carry the Opts suffix in both languages. This is the same operation as JS ClientFactory.withOpts, but the names diverge twice over: withOpts → _with_params swaps Opts for params and changes visibility (public + @internal in JS, underscore-private in Python), so the shared vocabulary downstream packages have to learn is different per language.
Pick one spelling and mirror it — withOpts ↔ with_opts if it stays part of the (documented-as-internal) cross-package surface, _withOpts ↔ _with_opts if it should be private in both. Same for the parameter name (api_params → opts) and _bound_api_params ↔ boundOpts. Not suggesting a patch inline because the rename touches the body and the callers in e2b/client.py, e2b_code_interpreter/client.py, and the tests.
…re SDKs untouched Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
Summary
Adds an
E2Bclient to@e2b/code-interpreterande2b-code-interpreter, so the API key/domain can be bound explicitly instead of coming from the environment.packages/js-sdkandpackages/python-sdkare untouched: the binding is local to this package and relies only on the existingClientFactoryclass state (boundOpts/_bound_api_params) the downstreamSandboxalready inherits.The mechanism, per package:
Options are copied (and the nested
headers/apiHeadersmaps snapshotted,signaldropped) so later mutations of the caller's object can't change what's bound.Precedence is the core one, unchanged: per-call options > client options > env; explicit
undefined/Nonedoes not clear a bound value. Clients are isolated from each other and from the env-configured top-level exports.Also: the new JS suite serves a mocked API from a local
node:httpserver, which workerd can't listen on, so it's excluded from the Cloudflare runtime leg (the Node/bun/deno legs keep running it).Link to Devin session: https://app.devin.ai/sessions/ec1af68649fd4880a1cb27cb51d819e9
Open in Devin Desktop: https://app.devin.ai/desktop/session/ec1af68649fd4880a1cb27cb51d819e9?variant=devin
Requested by: @mishushakov