abox-code-interpreter 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- abox_code_interpreter-0.1.0.dist-info/METADATA +39 -0
- abox_code_interpreter-0.1.0.dist-info/RECORD +12 -0
- abox_code_interpreter-0.1.0.dist-info/WHEEL +4 -0
- abox_code_interpreter-0.1.0.dist-info/licenses/LICENSE +10 -0
- abox_code_interpreter-0.1.0.dist-info/licenses/NOTICE +1 -0
- agentbox_code_interpreter/__init__.py +14 -0
- agentbox_code_interpreter/charts.py +232 -0
- agentbox_code_interpreter/code_interpreter_async.py +419 -0
- agentbox_code_interpreter/code_interpreter_sync.py +415 -0
- agentbox_code_interpreter/constants.py +3 -0
- agentbox_code_interpreter/exceptions.py +20 -0
- agentbox_code_interpreter/models.py +523 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from agentbox import TimeoutException, SandboxException
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import (
|
|
8
|
+
List,
|
|
9
|
+
Literal,
|
|
10
|
+
Optional,
|
|
11
|
+
Iterable,
|
|
12
|
+
Dict,
|
|
13
|
+
TypeVar,
|
|
14
|
+
Callable,
|
|
15
|
+
Awaitable,
|
|
16
|
+
Any,
|
|
17
|
+
Union,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from httpx import Response
|
|
21
|
+
|
|
22
|
+
from .charts import Chart, _deserialize_chart
|
|
23
|
+
|
|
24
|
+
RunCodeLanguage = Union[
|
|
25
|
+
Literal["python", "javascript", "typescript", "r", "java", "bash"],
|
|
26
|
+
str,
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
T = TypeVar("T")
|
|
30
|
+
OutputHandler = Union[Callable[[T], Any],]
|
|
31
|
+
|
|
32
|
+
OutputHandlerWithAsync = Union[
|
|
33
|
+
OutputHandler[T],
|
|
34
|
+
Callable[[T], Awaitable[Any]],
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class OutputMessage:
|
|
42
|
+
"""
|
|
43
|
+
Represents an output message from the sandbox code execution.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
line: str
|
|
47
|
+
"""
|
|
48
|
+
The output line.
|
|
49
|
+
"""
|
|
50
|
+
timestamp: int
|
|
51
|
+
"""
|
|
52
|
+
Unix epoch in nanoseconds
|
|
53
|
+
"""
|
|
54
|
+
error: bool = False
|
|
55
|
+
"""
|
|
56
|
+
Whether the output is an error.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __str__(self):
|
|
60
|
+
return self.line
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ExecutionError:
|
|
65
|
+
"""
|
|
66
|
+
Represents an error that occurred during the execution of a cell.
|
|
67
|
+
The error contains the name of the error, the value of the error, and the traceback.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
name: str
|
|
71
|
+
"""
|
|
72
|
+
Name of the error.
|
|
73
|
+
"""
|
|
74
|
+
value: str
|
|
75
|
+
"""
|
|
76
|
+
Value of the error.
|
|
77
|
+
"""
|
|
78
|
+
traceback: str
|
|
79
|
+
"""
|
|
80
|
+
The raw traceback of the error.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, name: str, value: str, traceback: str, **kwargs):
|
|
84
|
+
self.name = name
|
|
85
|
+
self.value = value
|
|
86
|
+
self.traceback = traceback
|
|
87
|
+
|
|
88
|
+
def to_json(self) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Returns the JSON representation of the Error object.
|
|
91
|
+
"""
|
|
92
|
+
data = {"name": self.name, "value": self.value, "traceback": self.traceback}
|
|
93
|
+
return json.dumps(data)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class MIMEType(str):
|
|
97
|
+
"""
|
|
98
|
+
Represents a MIME type.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass
|
|
103
|
+
class Result:
|
|
104
|
+
"""
|
|
105
|
+
Represents the data to be displayed as a result of executing a cell in a Jupyter notebook.
|
|
106
|
+
The result is similar to the structure returned by ipython kernel: https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics
|
|
107
|
+
|
|
108
|
+
The result can contain multiple types of data, such as text, images, plots, etc. Each type of data is represented
|
|
109
|
+
as a string, and the result can contain multiple types of data. The display calls don't have to have text representation,
|
|
110
|
+
for the actual result the representation is always present for the result, the other representations are always optional.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __getitem__(self, item):
|
|
114
|
+
return getattr(self, item)
|
|
115
|
+
|
|
116
|
+
text: Optional[str] = None
|
|
117
|
+
html: Optional[str] = None
|
|
118
|
+
markdown: Optional[str] = None
|
|
119
|
+
svg: Optional[str] = None
|
|
120
|
+
png: Optional[str] = None
|
|
121
|
+
jpeg: Optional[str] = None
|
|
122
|
+
pdf: Optional[str] = None
|
|
123
|
+
latex: Optional[str] = None
|
|
124
|
+
json: Optional[dict] = None
|
|
125
|
+
javascript: Optional[str] = None
|
|
126
|
+
data: Optional[dict] = None
|
|
127
|
+
chart: Optional[Chart] = None
|
|
128
|
+
is_main_result: bool = False
|
|
129
|
+
"""Whether this data is the result of the cell. Data can be produced by display calls of which can be multiple in a cell."""
|
|
130
|
+
extra: Optional[dict] = None
|
|
131
|
+
"""Extra data that can be included. Not part of the standard types."""
|
|
132
|
+
|
|
133
|
+
def __init__(
|
|
134
|
+
self,
|
|
135
|
+
text: Optional[str] = None,
|
|
136
|
+
html: Optional[str] = None,
|
|
137
|
+
markdown: Optional[str] = None,
|
|
138
|
+
svg: Optional[str] = None,
|
|
139
|
+
png: Optional[str] = None,
|
|
140
|
+
jpeg: Optional[str] = None,
|
|
141
|
+
pdf: Optional[str] = None,
|
|
142
|
+
latex: Optional[str] = None,
|
|
143
|
+
json: Optional[dict] = None,
|
|
144
|
+
javascript: Optional[str] = None,
|
|
145
|
+
data: Optional[dict] = None,
|
|
146
|
+
chart: Optional[dict] = None,
|
|
147
|
+
is_main_result: bool = False,
|
|
148
|
+
extra: Optional[dict] = None,
|
|
149
|
+
**kwargs, # Allows for future expansion
|
|
150
|
+
):
|
|
151
|
+
self.text = text
|
|
152
|
+
self.html = html
|
|
153
|
+
self.markdown = markdown
|
|
154
|
+
self.svg = svg
|
|
155
|
+
self.png = png
|
|
156
|
+
self.jpeg = jpeg
|
|
157
|
+
self.pdf = pdf
|
|
158
|
+
self.latex = latex
|
|
159
|
+
self.json = json
|
|
160
|
+
self.javascript = javascript
|
|
161
|
+
self.data = data
|
|
162
|
+
if chart:
|
|
163
|
+
try:
|
|
164
|
+
self.chart = _deserialize_chart(chart)
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(
|
|
167
|
+
f"Error deserializing chart, check if you are using the latest version of the library: {e}"
|
|
168
|
+
)
|
|
169
|
+
self.is_main_result = is_main_result
|
|
170
|
+
self.extra = extra
|
|
171
|
+
|
|
172
|
+
def formats(self) -> Iterable[str]:
|
|
173
|
+
"""
|
|
174
|
+
Returns all available formats of the result.
|
|
175
|
+
|
|
176
|
+
:return: All available formats of the result in MIME types.
|
|
177
|
+
"""
|
|
178
|
+
formats = []
|
|
179
|
+
if self.text:
|
|
180
|
+
formats.append("text")
|
|
181
|
+
if self.html:
|
|
182
|
+
formats.append("html")
|
|
183
|
+
if self.markdown:
|
|
184
|
+
formats.append("markdown")
|
|
185
|
+
if self.svg:
|
|
186
|
+
formats.append("svg")
|
|
187
|
+
if self.png:
|
|
188
|
+
formats.append("png")
|
|
189
|
+
if self.jpeg:
|
|
190
|
+
formats.append("jpeg")
|
|
191
|
+
if self.pdf:
|
|
192
|
+
formats.append("pdf")
|
|
193
|
+
if self.latex:
|
|
194
|
+
formats.append("latex")
|
|
195
|
+
if self.json:
|
|
196
|
+
formats.append("json")
|
|
197
|
+
if self.javascript:
|
|
198
|
+
formats.append("javascript")
|
|
199
|
+
if self.data:
|
|
200
|
+
formats.append("data")
|
|
201
|
+
if self.chart:
|
|
202
|
+
formats.append("chart")
|
|
203
|
+
|
|
204
|
+
if self.extra:
|
|
205
|
+
for key in self.extra:
|
|
206
|
+
formats.append(key)
|
|
207
|
+
|
|
208
|
+
return formats
|
|
209
|
+
|
|
210
|
+
def __str__(self) -> str:
|
|
211
|
+
"""
|
|
212
|
+
Returns the text representation of the data.
|
|
213
|
+
|
|
214
|
+
:return: The text representation of the data.
|
|
215
|
+
"""
|
|
216
|
+
return self.__repr__()
|
|
217
|
+
|
|
218
|
+
def __repr__(self) -> str:
|
|
219
|
+
if self.text:
|
|
220
|
+
return f"Result({self.text})"
|
|
221
|
+
else:
|
|
222
|
+
return "Result(Formats: " + ", ".join(self.formats()) + ")"
|
|
223
|
+
|
|
224
|
+
def _repr_html_(self) -> Optional[str]:
|
|
225
|
+
"""
|
|
226
|
+
Returns the HTML representation of the data.
|
|
227
|
+
|
|
228
|
+
:return: The HTML representation of the data.
|
|
229
|
+
"""
|
|
230
|
+
return self.html
|
|
231
|
+
|
|
232
|
+
def _repr_markdown_(self) -> Optional[str]:
|
|
233
|
+
"""
|
|
234
|
+
Returns the Markdown representation of the data.
|
|
235
|
+
|
|
236
|
+
:return: The Markdown representation of the data.
|
|
237
|
+
"""
|
|
238
|
+
return self.markdown
|
|
239
|
+
|
|
240
|
+
def _repr_svg_(self) -> Optional[str]:
|
|
241
|
+
"""
|
|
242
|
+
Returns the SVG representation of the data.
|
|
243
|
+
|
|
244
|
+
:return: The SVG representation of the data.
|
|
245
|
+
"""
|
|
246
|
+
return self.svg
|
|
247
|
+
|
|
248
|
+
def _repr_png_(self) -> Optional[str]:
|
|
249
|
+
"""
|
|
250
|
+
Returns the base64 representation of the PNG data.
|
|
251
|
+
|
|
252
|
+
:return: The base64 representation of the PNG data.
|
|
253
|
+
"""
|
|
254
|
+
return self.png
|
|
255
|
+
|
|
256
|
+
def _repr_jpeg_(self) -> Optional[str]:
|
|
257
|
+
"""
|
|
258
|
+
Returns the base64 representation of the JPEG data.
|
|
259
|
+
|
|
260
|
+
:return: The base64 representation of the JPEG data.
|
|
261
|
+
"""
|
|
262
|
+
return self.jpeg
|
|
263
|
+
|
|
264
|
+
def _repr_pdf_(self) -> Optional[str]:
|
|
265
|
+
"""
|
|
266
|
+
Returns the PDF representation of the data.
|
|
267
|
+
|
|
268
|
+
:return: The PDF representation of the data.
|
|
269
|
+
"""
|
|
270
|
+
return self.pdf
|
|
271
|
+
|
|
272
|
+
def _repr_latex_(self) -> Optional[str]:
|
|
273
|
+
"""
|
|
274
|
+
Returns the LaTeX representation of the data.
|
|
275
|
+
|
|
276
|
+
:return: The LaTeX representation of the data.
|
|
277
|
+
"""
|
|
278
|
+
return self.latex
|
|
279
|
+
|
|
280
|
+
def _repr_json_(self) -> Optional[dict]:
|
|
281
|
+
"""
|
|
282
|
+
Returns the JSON representation of the data.
|
|
283
|
+
|
|
284
|
+
:return: The JSON representation of the data.
|
|
285
|
+
"""
|
|
286
|
+
return self.json
|
|
287
|
+
|
|
288
|
+
def _repr_javascript_(self) -> Optional[str]:
|
|
289
|
+
"""
|
|
290
|
+
Returns the JavaScript representation of the data.
|
|
291
|
+
|
|
292
|
+
:return: The JavaScript representation of the data.
|
|
293
|
+
"""
|
|
294
|
+
return self.javascript
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@dataclass(repr=False)
|
|
298
|
+
class Logs:
|
|
299
|
+
"""
|
|
300
|
+
Data printed to stdout and stderr during execution, usually by print statements, logs, warnings, subprocesses, etc.
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
stdout: List[str] = field(default_factory=list)
|
|
304
|
+
"""List of strings printed to stdout by prints, subprocesses, etc."""
|
|
305
|
+
stderr: List[str] = field(default_factory=list)
|
|
306
|
+
"""List of strings printed to stderr by prints, subprocesses, etc."""
|
|
307
|
+
|
|
308
|
+
def __init__(
|
|
309
|
+
self,
|
|
310
|
+
stdout: Optional[List[str]] = None,
|
|
311
|
+
stderr: Optional[List[str]] = None,
|
|
312
|
+
**kwargs,
|
|
313
|
+
):
|
|
314
|
+
self.stdout = stdout or []
|
|
315
|
+
self.stderr = stderr or []
|
|
316
|
+
|
|
317
|
+
def __repr__(self):
|
|
318
|
+
return f"Logs(stdout: {self.stdout}, stderr: {self.stderr})"
|
|
319
|
+
|
|
320
|
+
def to_json(self) -> str:
|
|
321
|
+
"""
|
|
322
|
+
Returns the JSON representation of the Logs object.
|
|
323
|
+
"""
|
|
324
|
+
data = {"stdout": self.stdout, "stderr": self.stderr}
|
|
325
|
+
return json.dumps(data)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def serialize_results(results: List[Result]) -> List[Dict[str, str]]:
|
|
329
|
+
"""
|
|
330
|
+
Serializes the results to JSON.
|
|
331
|
+
"""
|
|
332
|
+
serialized = []
|
|
333
|
+
for result in results:
|
|
334
|
+
serialized_dict = {}
|
|
335
|
+
for key in result.formats():
|
|
336
|
+
if key == "chart":
|
|
337
|
+
chart = result.chart
|
|
338
|
+
if chart is not None:
|
|
339
|
+
serialized_dict[key] = chart.to_dict()
|
|
340
|
+
else:
|
|
341
|
+
serialized_dict[key] = result[key]
|
|
342
|
+
|
|
343
|
+
serialized_dict["text"] = result.text
|
|
344
|
+
serialized.append(serialized_dict)
|
|
345
|
+
|
|
346
|
+
return serialized
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@dataclass(repr=False)
|
|
350
|
+
class Execution:
|
|
351
|
+
"""
|
|
352
|
+
Represents the result of a cell execution.
|
|
353
|
+
"""
|
|
354
|
+
|
|
355
|
+
results: List[Result] = field(default_factory=list)
|
|
356
|
+
"""List of the result of the cell (interactively interpreted last line), display calls (e.g. matplotlib plots)."""
|
|
357
|
+
logs: Logs = field(default_factory=Logs)
|
|
358
|
+
"""Logs printed to stdout and stderr during execution."""
|
|
359
|
+
error: Optional[ExecutionError] = None
|
|
360
|
+
"""Error object if an error occurred, None otherwise."""
|
|
361
|
+
execution_count: Optional[int] = None
|
|
362
|
+
"""Execution count of the cell."""
|
|
363
|
+
|
|
364
|
+
def __init__(
|
|
365
|
+
self,
|
|
366
|
+
results: Optional[List[Result]] = None,
|
|
367
|
+
logs: Optional[Logs] = None,
|
|
368
|
+
error: Optional[ExecutionError] = None,
|
|
369
|
+
execution_count: Optional[int] = None,
|
|
370
|
+
**kwargs,
|
|
371
|
+
):
|
|
372
|
+
self.results = results or []
|
|
373
|
+
self.logs = logs or Logs()
|
|
374
|
+
self.error = error
|
|
375
|
+
self.execution_count = execution_count
|
|
376
|
+
|
|
377
|
+
def __repr__(self):
|
|
378
|
+
return f"Execution(Results: {self.results}, Logs: {self.logs}, Error: {self.error})"
|
|
379
|
+
|
|
380
|
+
@property
|
|
381
|
+
def text(self) -> Optional[str]:
|
|
382
|
+
"""
|
|
383
|
+
Returns the text representation of the result.
|
|
384
|
+
|
|
385
|
+
:return: The text representation of the result.
|
|
386
|
+
"""
|
|
387
|
+
for d in self.results:
|
|
388
|
+
if d.is_main_result:
|
|
389
|
+
return d.text
|
|
390
|
+
|
|
391
|
+
def to_json(self) -> str:
|
|
392
|
+
"""
|
|
393
|
+
Returns the JSON representation of the Execution object.
|
|
394
|
+
"""
|
|
395
|
+
data = {
|
|
396
|
+
"results": serialize_results(self.results),
|
|
397
|
+
"logs": self.logs.to_json(),
|
|
398
|
+
"error": self.error.to_json() if self.error else None,
|
|
399
|
+
}
|
|
400
|
+
return json.dumps(data)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
async def aextract_exception(res: Response):
|
|
404
|
+
if res.is_success:
|
|
405
|
+
return None
|
|
406
|
+
|
|
407
|
+
await res.aread()
|
|
408
|
+
return extract_exception(res)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def extract_exception(res: Response):
|
|
412
|
+
if res.is_success:
|
|
413
|
+
return None
|
|
414
|
+
|
|
415
|
+
res.read()
|
|
416
|
+
return format_exception(res)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def format_exception(res: Response):
|
|
420
|
+
if res.is_success:
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
if res.status_code == 404:
|
|
424
|
+
return SandboxException(res.text)
|
|
425
|
+
elif res.status_code == 502:
|
|
426
|
+
return TimeoutException(
|
|
427
|
+
f"{res.text}: This error is likely due to sandbox timeout. You can modify the sandbox timeout by passing 'timeout' when starting the sandbox or calling '.set_timeout' on the sandbox with the desired timeout."
|
|
428
|
+
)
|
|
429
|
+
else:
|
|
430
|
+
return SandboxException(f"{res.status_code}: {res.text}")
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def parse_output(
|
|
434
|
+
execution: Execution,
|
|
435
|
+
output: str,
|
|
436
|
+
on_stdout: Optional[OutputHandler[OutputMessage]] = None,
|
|
437
|
+
on_stderr: Optional[OutputHandler[OutputMessage]] = None,
|
|
438
|
+
on_result: Optional[OutputHandler[Result]] = None,
|
|
439
|
+
on_error: Optional[OutputHandler[ExecutionError]] = None,
|
|
440
|
+
):
|
|
441
|
+
_parse_output(execution, output, on_stdout, on_stderr, on_result, on_error)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
async def async_parse_output(
|
|
445
|
+
execution: Execution,
|
|
446
|
+
output: str,
|
|
447
|
+
on_stdout: Optional[OutputHandlerWithAsync[OutputMessage]] = None,
|
|
448
|
+
on_stderr: Optional[OutputHandlerWithAsync[OutputMessage]] = None,
|
|
449
|
+
on_result: Optional[OutputHandlerWithAsync[Result]] = None,
|
|
450
|
+
on_error: Optional[OutputHandlerWithAsync[ExecutionError]] = None,
|
|
451
|
+
):
|
|
452
|
+
none_or_awaitable = _parse_output(
|
|
453
|
+
execution, output, on_stdout, on_stderr, on_result, on_error
|
|
454
|
+
)
|
|
455
|
+
if inspect.isawaitable(none_or_awaitable):
|
|
456
|
+
await none_or_awaitable
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _parse_output(
|
|
460
|
+
execution: Execution,
|
|
461
|
+
output: str,
|
|
462
|
+
on_stdout: Optional[OutputHandler[OutputMessage]] = None,
|
|
463
|
+
on_stderr: Optional[OutputHandler[OutputMessage]] = None,
|
|
464
|
+
on_result: Optional[OutputHandler[Result]] = None,
|
|
465
|
+
on_error: Optional[OutputHandler[ExecutionError]] = None,
|
|
466
|
+
) -> Union[None, Awaitable[Any]]:
|
|
467
|
+
data = json.loads(output)
|
|
468
|
+
data_type = data.pop("type")
|
|
469
|
+
|
|
470
|
+
if data_type == "result":
|
|
471
|
+
result = Result(**data)
|
|
472
|
+
execution.results.append(result)
|
|
473
|
+
if on_result:
|
|
474
|
+
return on_result(result)
|
|
475
|
+
elif data_type == "stdout":
|
|
476
|
+
execution.logs.stdout.append(data["text"])
|
|
477
|
+
if on_stdout:
|
|
478
|
+
return on_stdout(OutputMessage(data["text"], data["timestamp"], False))
|
|
479
|
+
elif data_type == "stderr":
|
|
480
|
+
execution.logs.stderr.append(data["text"])
|
|
481
|
+
if on_stderr:
|
|
482
|
+
return on_stderr(OutputMessage(data["text"], data["timestamp"], True))
|
|
483
|
+
elif data_type == "error":
|
|
484
|
+
execution.error = ExecutionError(data["name"], data["value"], data["traceback"])
|
|
485
|
+
if on_error:
|
|
486
|
+
return on_error(execution.error)
|
|
487
|
+
elif data_type == "number_of_executions":
|
|
488
|
+
execution.execution_count = data["execution_count"]
|
|
489
|
+
|
|
490
|
+
return None
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
@dataclass
|
|
494
|
+
class Context:
|
|
495
|
+
"""
|
|
496
|
+
Represents a context for code execution.
|
|
497
|
+
"""
|
|
498
|
+
|
|
499
|
+
id: str
|
|
500
|
+
"""
|
|
501
|
+
The ID of the context.
|
|
502
|
+
"""
|
|
503
|
+
language: str
|
|
504
|
+
"""
|
|
505
|
+
The language of the context.
|
|
506
|
+
"""
|
|
507
|
+
cwd: str
|
|
508
|
+
"""
|
|
509
|
+
The working directory of the context.
|
|
510
|
+
"""
|
|
511
|
+
|
|
512
|
+
def __init__(self, context_id: str, language: str, cwd: str, **kwargs):
|
|
513
|
+
self.id = context_id
|
|
514
|
+
self.language = language
|
|
515
|
+
self.cwd = cwd
|
|
516
|
+
|
|
517
|
+
@classmethod
|
|
518
|
+
def from_json(cls, data: Dict[str, str]):
|
|
519
|
+
return cls(
|
|
520
|
+
context_id=data["id"],
|
|
521
|
+
language=data["language"],
|
|
522
|
+
cwd=data["cwd"],
|
|
523
|
+
)
|