Repository navigation
fix(code-interpreter): collect non-standard MIME types into Result.extra #1985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@e2b/code-interpreter-python': patch | ||
| --- | ||
|
|
||
| fix(code-interpreter): collect non-standard MIME types into `Result.extra` in the Python SDK — `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, so non-standard representations (e.g. `application/vnd.plotly.v1+json`, bokeh, vega-lite) were silently lost. They are now swept into `extra`, listed by `formats()`, and preserved by `Execution.to_json()`, matching the JS SDK's `Result` constructor. `serialize_results` reads extra keys from `Result.extra` rather than treating them as attributes. |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -167,7 +167,14 @@ def __init__( | |||||||||
| f"Error deserializing chart, check if you are using the latest version of the library: {e}" | ||||||||||
| ) | ||||||||||
| self.is_main_result = is_main_result | ||||||||||
| self.extra = extra | ||||||||||
| # Collect non-standard MIME types (e.g. application/vnd.plotly.v1+json) | ||||||||||
| # into `extra`, matching the JS SDK's Result constructor, which sweeps | ||||||||||
| # every key outside its known set into `extra`. Without this the | ||||||||||
| # kernel's non-standard representations are silently dropped. | ||||||||||
| if kwargs: | ||||||||||
| self.extra = {**(extra or {}), **kwargs} | ||||||||||
| else: | ||||||||||
| self.extra = extra | ||||||||||
|
|
||||||||||
| def formats(self) -> Iterable[str]: | ||||||||||
| """ | ||||||||||
|
|
@@ -337,6 +344,8 @@ def serialize_results(results: List[Result]) -> List[Dict[str, str]]: | |||||||||
| chart = result.chart | ||||||||||
| if chart is not None: | ||||||||||
| serialized_dict[key] = chart.to_dict() | ||||||||||
| elif result.extra is not None and key in result.extra: | ||||||||||
| serialized_dict[key] = result.extra[key] | ||||||||||
|
Comment on lines
+347
to
+348
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 ( Use JS's nested shape:
Suggested change
Comment on lines
+347
to
+348
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Extra HTML replaces the actual result When Learn more
Example: Recommended fix: Keep standard fields authoritative in Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||
| else: | ||||||||||
| serialized_dict[key] = result[key] | ||||||||||
|
|
||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||||
| import json | ||||||
|
|
||||||
| from e2b_code_interpreter.models import Execution, Result, parse_output | ||||||
|
|
||||||
|
|
||||||
| def _result_from(message: dict) -> Result: | ||||||
| execution = Execution() | ||||||
| parse_output( | ||||||
| execution, json.dumps({"type": "result", "is_main_result": True, **message}) | ||||||
| ) | ||||||
| return execution.results[0] | ||||||
|
|
||||||
|
|
||||||
| def test_collects_non_standard_mime_types_into_extra(): | ||||||
| # The IPython kernel sends non-standard MIME types (e.g. plotly/bokeh) that | ||||||
| # aren't among the known representations. They should land in `extra`, like | ||||||
| # the JS SDK's Result constructor does — not be silently dropped. | ||||||
| result = _result_from( | ||||||
| {"text": "hello", "application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}} | ||||||
| ) | ||||||
|
|
||||||
| assert result.extra == {"application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}} | ||||||
|
|
||||||
|
|
||||||
| def test_formats_lists_non_standard_mime_types(): | ||||||
| result = _result_from( | ||||||
| {"text": "hello", "application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}} | ||||||
| ) | ||||||
|
|
||||||
| assert "application/vnd.plotly.v1+json" in list(result.formats()) | ||||||
|
|
||||||
|
|
||||||
| def test_to_json_preserves_non_standard_mime_types(): | ||||||
| execution = Execution() | ||||||
| parse_output( | ||||||
| execution, | ||||||
| json.dumps( | ||||||
| { | ||||||
| "type": "result", | ||||||
| "is_main_result": True, | ||||||
| "text": "hello", | ||||||
| "application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}, | ||||||
| } | ||||||
| ), | ||||||
| ) | ||||||
|
|
||||||
| assert json.loads(execution.to_json())["results"] == [ | ||||||
| { | ||||||
| "text": "hello", | ||||||
| "application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. T-1 (follow-up to the
Suggested change
|
||||||
| } | ||||||
| ] | ||||||
|
|
||||||
|
|
||||||
| def test_result_without_extra_fields_has_no_extra(): | ||||||
| result = _result_from({"text": "hello"}) | ||||||
|
|
||||||
| assert result.extra is None | ||||||
| assert list(result.formats()) == ["text"] | ||||||
|
|
||||||
|
|
||||||
| def test_explicit_extra_and_unknown_kwargs_are_merged(): | ||||||
| # A non-standard MIME type arriving via the parsed message and an explicit | ||||||
| # `extra` passed to the constructor should both be retained. | ||||||
| result = Result( | ||||||
| text="hello", | ||||||
| extra={"application/vnd.custom+json": {"y": 2}}, | ||||||
| **{"application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}}, | ||||||
| ) | ||||||
|
|
||||||
| assert result.extra == { | ||||||
| "application/vnd.custom+json": {"y": 2}, | ||||||
| "application/vnd.plotly.v1+json": {"data": [{"x": [1, 2]}]}, | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Plain metadata appears as a MIME format
When a result message includes
plain,Result.extraretains it as a custom format. JS Result excludesplain, so Python now advertises a non-MIME key informats().Learn more
_parse_outputpasses every result message field intoResult. The JS result constructor treatsplainas a known field and skips it when collecting extra representations, but the Python sweep accepts it.formats()then reportsplainandExecution.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 omitsplainfrom extra and formats.Recommended fix: Exclude protocol-only keys such as
plainfromkwargsbefore merging MIME representations intoextra, matching the JS constructor's known-key list.Was this helpful? React with 👍 or 👎 to provide feedback.