Repository navigation
Conversation
The Python SDK's `Result.__init__` declared an `extra` field for data "not part of the standard types", but unknown keys from the kernel message were dropped via `**kwargs` instead of collected into `extra`. Non-standard representations the IPython kernel sends — e.g. `application/vnd.plotly.v1+json`, bokeh, vega-lite — were silently lost, while the JS SDK's `Result` constructor already sweeps them into `extra`. Collect `**kwargs` into `extra` (merged with an explicitly passed `extra`), so non-standard MIME types are retained, listed by `formats()`, and preserved by `Execution.to_json()`. `serialize_results` reads extra keys from `Result.extra` rather than treating them as object attributes. Co-Authored-By: Kscc <noreply@owtffssent.com>
🦋 Changeset detectedLatest commit: ace7cd7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
TASTE.md compliance review (SDK design principles, e2b-dev/sdk-harness).
Checked: T-1 (cross-SDK parity), T-16 (dataclass results / dict options), T-18 (boundary mapping), T-62/T-63 (errors), T-69/T-71 (docstrings).
Violations: 1 (2 inline comments, same root cause)
- T-1: The constructor now sweeps unknown keys into
extra, which matches JS.to_json(), however, flattens those keys into the top level of each result, while JSResult.toJSON()nests them underextra. The new test asserts the flattened shape, so it needs updating too.
No other violations in the changed lines. The Result.extra default of None (JS uses {}) was already there before this PR and isn't counted.
| elif result.extra is not None and key in result.extra: | ||
| serialized_dict[key] = result.extra[key] |
There was a problem hiding this comment.
T-1 — JS, sync Python, and async Python must mirror each other 1:1 in semantics, not just names.
This flattens each non-standard MIME type into the top level of the serialized result ({"text": ..., "application/vnd.plotly.v1+json": ...}). JS Result.toJSON() (packages/code-interpreter-js/src/messaging.ts) nests them instead: ...(Object.keys(this.extra).length > 0 ? { extra: this.extra } : {}). So the same execution would serialize to two different shapes depending on the SDK, which goes against the PR's own goal of "matching the JS SDK". It also lets an arbitrary kernel MIME key share a namespace with the standard keys (text, html, …).
Use JS's nested shape:
| elif result.extra is not None and key in result.extra: | |
| serialized_dict[key] = result.extra[key] | |
| elif result.extra is not None and key in result.extra: | |
| serialized_dict.setdefault("extra", {})[key] = result.extra[key] |
| assert json.loads(execution.to_json())["results"] == [ | ||
| { | ||
| "text": "hello", | ||
| "application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}, |
There was a problem hiding this comment.
T-1 (follow-up to the serialize_results comment) — this assertion locks in the flattened shape, which differs from JS Result.toJSON(). That method emits non-standard MIME types under an extra key. To match JS:
| "application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}, | |
| "extra": {"application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}}, |
There was a problem hiding this comment.
Devin Review found 2 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| elif result.extra is not None and key in result.extra: | ||
| serialized_dict[key] = result.extra[key] |
There was a problem hiding this comment.
🟡 Extra HTML replaces the actual result
When extra contains html, serialize_results writes that value instead of result.html. The serialized execution contains different HTML from the result users received.
Learn more
Result.formats() lists standard fields and then keys in extra, including duplicate names. Previously, both passes through serialize_results read the standard attribute, so an explicit extra key could not replace the result's HTML. The new branch selects extra for both passes and overwrites the standard value. The same collision affects other standard fields besides text, which is reset later.
Example: Execution(results=[Result(html='<b>new</b>', extra={'html': '<i>old</i>'})]).to_json() now contains "html": "<i>old</i>", while result.html remains <b>new</b>.
Recommended fix: Keep standard fields authoritative in serialize_results, and only read result.extra[key] for keys not represented by standard fields; account for duplicate keys from formats().
Was this helpful? React with 👍 or 👎 to provide feedback.
| if kwargs: | ||
| self.extra = {**(extra or {}), **kwargs} |
There was a problem hiding this comment.
🟡 Plain metadata appears as a MIME format
When a result message includes plain, Result.extra retains it as a custom format. JS Result excludes plain, so Python now advertises a non-MIME key in formats().
Learn more
_parse_output passes every result message field into Result. The JS result constructor treats plain as a known field and skips it when collecting extra representations, but the Python sweep accepts it. formats() then reports plain and Execution.to_json() includes it among the result representations.
Example: For a result message {'type': 'result', 'text': 'hello', 'plain': 'hello'}, Python reports ['text', 'plain']; the JS result omits plain from extra and formats.
Recommended fix: Exclude protocol-only keys such as plain from kwargs before merging MIME representations into extra, matching the JS constructor's known-key list.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
The Python SDK's
Result.__init__declared anextrafield for data "not part of the standard types", but unknown keys from the kernel message were dropped via**kwargsinstead of collected intoextra. Non-standard MIME types the IPython kernel sends — e.g.application/vnd.plotly.v1+json, bokeh, vega-lite — were silently lost, while the JS SDK'sResultconstructor already sweeps them intoextra. Fixes #1984.The change
Result.__init__now collects**kwargsintoextra(merged with an explicitly passedextra), so non-standard MIME types are retained. Becauseformats()already iteratesself.extra, the custom MIME types are now listed byformats().serialize_resultsreads extra keys fromResult.extrarather than treating them as object attributes (which raisedAttributeErroronce extra keys enteredformats()).if key == "chart": ... + elif result.extra is not None and key in result.extra: + serialized_dict[key] = result.extra[key] else: serialized_dict[key] = result[key]Usage example
This matches the JS SDK, whose
Resultconstructor already sweeps unknown keys intoextra:Testing
Adds
packages/code-interpreter-python/tests/test_result_extra.py(no sandbox needed): collects a non-standard MIME type intoextra, lists it informats(), preserves it throughto_json(), leavesextraempty for a bare result, and merges an explicitextrawith unknown kwargs. All 18 no-credential tests pass (5 new + 13 existingtest_deserialize.py).ruff format/ruff checkclean.Scope
Python-only — the JS SDK already does the right thing. This is the Python side of the
Resultserialization surface and does not overlap the open cluster: #1939/#1971 (dataintoJSON), #1948 (chartintoJSON), #1826/#1893 (chart deserialization). None touch PythonResult.__init__'s extra collection.🤖 Generated with kscc