Skip to content

Commit e4cb329

Browse files
committed
feat: Introduce code interpreter module with models, sync/async execution, and desktop component
1 parent b1b67d1 commit e4cb329

12 files changed

Lines changed: 2227 additions & 3 deletions

File tree

README.md

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,50 @@ pip install ucloud_sandbox
2020
export UCLOUD_SANDBOX_API_KEY=your_api_key
2121
```
2222

23-
### 2. 运行代码
23+
### 2. 基础沙箱
2424

2525
```python
2626
from ucloud_sandbox import Sandbox
2727

28+
with Sandbox.create() as sandbox:
29+
result = sandbox.commands.run("echo 'Hello, World!'")
30+
print(result.stdout)
31+
```
32+
33+
### 3. Code Interpreter(代码解释器)
34+
35+
支持有状态的代码执行,变量在多次调用之间保持:
36+
37+
```python
38+
from ucloud_sandbox.code_interpreter import Sandbox
39+
2840
with Sandbox.create() as sandbox:
2941
sandbox.run_code("x = 1")
30-
execution = sandbox.run_code("x += 1; x")
31-
print(execution.text) # 输出: 2
42+
execution = sandbox.run_code("x += 1; print(x)")
43+
print(execution.logs.stdout) # ['2']
44+
```
45+
46+
### 4. Desktop(桌面环境)
47+
48+
支持鼠标键盘控制、截图、VNC 流媒体:
49+
50+
```python
51+
from ucloud_sandbox.desktop import Sandbox
52+
53+
desktop = Sandbox.create()
54+
55+
# 截图
56+
screenshot = desktop.screenshot()
57+
58+
# 鼠标操作
59+
desktop.left_click(100, 200)
60+
desktop.write("Hello, World!")
61+
62+
# VNC 流
63+
desktop.stream.start()
64+
print(desktop.stream.get_url())
65+
66+
desktop.kill()
3267
```
3368

3469
## 文档

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ packaging = ">=24.1"
2121
typing-extensions = ">=4.1.0"
2222
dockerfile-parse = "^2.0.1"
2323
rich = ">=14.0.0"
24+
requests = "^2.32.3"
25+
pillow = "^11.1.0"
2426

2527
[tool.poetry.group.dev.dependencies]
2628
pytest = "^7.4.0"

ucloud_sandbox/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@
9292
from .template_async.main import AsyncTemplate
9393
from .template_sync.main import Template
9494

95+
# Code Interpreter and Desktop modules are available as submodules:
96+
# from ucloud_sandbox.code_interpreter import Sandbox
97+
# from ucloud_sandbox.desktop import Sandbox
98+
9599
__all__ = [
96100
# API
97101
"ApiClient",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""
2+
UCloud Sandbox Code Interpreter - Stateful code execution in cloud sandbox.
3+
4+
The Code Interpreter allows you to:
5+
- Run Python and other code in a stateful Jupyter-like environment
6+
- Create and manage execution contexts
7+
- Get rich output including text, images, charts, and more
8+
9+
Example:
10+
```python
11+
from ucloud_sandbox import CodeInterpreter
12+
13+
sandbox = CodeInterpreter.create()
14+
execution = sandbox.run_code("print('Hello, World!')")
15+
print(execution.logs.stdout)
16+
```
17+
"""
18+
19+
from .charts import (
20+
Chart,
21+
ChartType,
22+
Chart2D,
23+
LineChart,
24+
ScatterChart,
25+
BarChart,
26+
PieChart,
27+
BoxAndWhiskerChart,
28+
SuperChart,
29+
ScaleType,
30+
)
31+
from .models import (
32+
Context,
33+
Execution,
34+
ExecutionError,
35+
Result,
36+
MIMEType,
37+
Logs,
38+
OutputHandler,
39+
OutputMessage,
40+
)
41+
from .code_interpreter_sync import Sandbox
42+
from .code_interpreter_async import AsyncSandbox
43+
44+
__all__ = [
45+
# Main classes
46+
"Sandbox",
47+
"AsyncSandbox",
48+
# Models
49+
"Context",
50+
"Execution",
51+
"ExecutionError",
52+
"Result",
53+
"MIMEType",
54+
"Logs",
55+
"OutputHandler",
56+
"OutputMessage",
57+
# Charts
58+
"Chart",
59+
"ChartType",
60+
"Chart2D",
61+
"LineChart",
62+
"ScatterChart",
63+
"BarChart",
64+
"PieChart",
65+
"BoxAndWhiskerChart",
66+
"SuperChart",
67+
"ScaleType",
68+
]
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
"""
2+
Chart data models for Code Interpreter.
3+
4+
These models represent extracted chart data from matplotlib and other plotting libraries,
5+
useful for building interactive charts or custom visualizations.
6+
"""
7+
8+
import enum
9+
from typing import Any, List, Tuple, Optional, Union
10+
11+
12+
class ChartType(str, enum.Enum):
13+
"""
14+
Chart types
15+
"""
16+
17+
LINE = "line"
18+
SCATTER = "scatter"
19+
BAR = "bar"
20+
PIE = "pie"
21+
BOX_AND_WHISKER = "box_and_whisker"
22+
SUPERCHART = "superchart"
23+
UNKNOWN = "unknown"
24+
25+
26+
class ScaleType(str, enum.Enum):
27+
"""
28+
Ax scale types
29+
"""
30+
31+
LINEAR = "linear"
32+
DATETIME = "datetime"
33+
CATEGORICAL = "categorical"
34+
LOG = "log"
35+
SYMLOG = "symlog"
36+
LOGIT = "logit"
37+
FUNCTION = "function"
38+
FUNCTIONLOG = "functionlog"
39+
ASINH = "asinh"
40+
UNKNOWN = "unknown"
41+
42+
43+
class Chart:
44+
"""
45+
Extracted data from a chart. It's useful for building interactive charts or custom visualizations.
46+
"""
47+
48+
type: ChartType
49+
title: str
50+
elements: List[Any]
51+
52+
def __init__(self, **kwargs):
53+
self._raw_data = kwargs
54+
self.type = ChartType(kwargs.get("type") or ChartType.UNKNOWN)
55+
self.title = kwargs.get("title", "")
56+
self.elements = kwargs.get("elements", [])
57+
58+
def to_dict(self) -> dict:
59+
return self._raw_data
60+
61+
62+
class Chart2D(Chart):
63+
x_label: Optional[str]
64+
y_label: Optional[str]
65+
x_unit: Optional[str]
66+
y_unit: Optional[str]
67+
68+
def __init__(self, **kwargs):
69+
super().__init__(**kwargs)
70+
self.x_label = kwargs.get("x_label")
71+
self.y_label = kwargs.get("y_label")
72+
self.x_unit = kwargs.get("x_unit")
73+
self.y_unit = kwargs.get("y_unit")
74+
75+
76+
class PointData:
77+
label: str
78+
points: List[Tuple[Union[str, float], Union[str, float]]]
79+
80+
def __init__(self, **kwargs):
81+
self.label = kwargs.get("label", "")
82+
self.points = [(x, y) for x, y in kwargs.get("points", [])]
83+
84+
85+
class PointChart(Chart2D):
86+
x_ticks: List[Union[str, float]]
87+
x_tick_labels: List[str]
88+
x_scale: ScaleType
89+
90+
y_ticks: List[Union[str, float]]
91+
y_tick_labels: List[str]
92+
y_scale: ScaleType
93+
94+
elements: List[PointData]
95+
96+
def __init__(self, **kwargs):
97+
super().__init__(**kwargs)
98+
self.x_label = kwargs.get("x_label")
99+
100+
try:
101+
self.x_scale = ScaleType(kwargs.get("x_scale"))
102+
except ValueError:
103+
self.x_scale = ScaleType.UNKNOWN
104+
105+
self.x_ticks = kwargs.get("x_ticks", [])
106+
self.x_tick_labels = kwargs.get("x_tick_labels", [])
107+
108+
self.y_label = kwargs.get("y_label")
109+
110+
try:
111+
self.y_scale = ScaleType(kwargs.get("y_scale"))
112+
except ValueError:
113+
self.y_scale = ScaleType.UNKNOWN
114+
115+
self.y_ticks = kwargs.get("y_ticks", [])
116+
self.y_tick_labels = kwargs.get("y_tick_labels", [])
117+
118+
self.elements = [PointData(**d) for d in kwargs.get("elements", [])]
119+
120+
121+
class LineChart(PointChart):
122+
type = ChartType.LINE
123+
124+
125+
class ScatterChart(PointChart):
126+
type = ChartType.SCATTER
127+
128+
129+
class BarData:
130+
label: str
131+
group: str
132+
value: str
133+
134+
def __init__(self, **kwargs):
135+
self.label = kwargs.get("label", "")
136+
self.value = kwargs.get("value", "")
137+
self.group = kwargs.get("group", "")
138+
139+
140+
class BarChart(Chart2D):
141+
type = ChartType.BAR
142+
elements: List[BarData]
143+
144+
def __init__(self, **kwargs):
145+
super().__init__(**kwargs)
146+
self.elements = [BarData(**d) for d in kwargs.get("elements", [])]
147+
148+
149+
class PieData:
150+
label: str
151+
angle: float
152+
radius: float
153+
154+
def __init__(self, **kwargs):
155+
self.label = kwargs.get("label", "")
156+
self.angle = kwargs.get("angle", 0.0)
157+
self.radius = kwargs.get("radius", 0.0)
158+
159+
160+
class PieChart(Chart):
161+
type = ChartType.PIE
162+
elements: List[PieData]
163+
164+
def __init__(self, **kwargs):
165+
super().__init__(**kwargs)
166+
self.elements = [PieData(**d) for d in kwargs.get("elements", [])]
167+
168+
169+
class BoxAndWhiskerData:
170+
label: str
171+
min: float
172+
first_quartile: float
173+
median: float
174+
third_quartile: float
175+
max: float
176+
outliers: List[float]
177+
178+
def __init__(self, **kwargs):
179+
self.label = kwargs.get("label", "")
180+
self.min = kwargs.get("min", 0.0)
181+
self.first_quartile = kwargs.get("first_quartile", 0.0)
182+
self.median = kwargs.get("median", 0.0)
183+
self.third_quartile = kwargs.get("third_quartile", 0.0)
184+
self.max = kwargs.get("max", 0.0)
185+
self.outliers = kwargs.get("outliers") or []
186+
187+
188+
class BoxAndWhiskerChart(Chart2D):
189+
type = ChartType.BOX_AND_WHISKER
190+
elements: List[BoxAndWhiskerData]
191+
192+
def __init__(self, **kwargs):
193+
super().__init__(**kwargs)
194+
self.elements = [BoxAndWhiskerData(**d) for d in kwargs.get("elements", [])]
195+
196+
197+
class SuperChart(Chart):
198+
type = ChartType.SUPERCHART
199+
elements: List[
200+
Union[LineChart, ScatterChart, BarChart, PieChart, BoxAndWhiskerChart]
201+
]
202+
203+
def __init__(self, **kwargs):
204+
super().__init__(**kwargs)
205+
self.elements = [_deserialize_chart(g) for g in kwargs.get("elements", [])]
206+
207+
208+
ChartTypes = Union[
209+
LineChart, ScatterChart, BarChart, PieChart, BoxAndWhiskerChart, SuperChart
210+
]
211+
212+
213+
def _deserialize_chart(data: Optional[dict]) -> Optional[ChartTypes]:
214+
if not data:
215+
return None
216+
217+
chart_type = data.get("type")
218+
if chart_type == ChartType.LINE:
219+
chart = LineChart(**data)
220+
elif chart_type == ChartType.SCATTER:
221+
chart = ScatterChart(**data)
222+
elif chart_type == ChartType.BAR:
223+
chart = BarChart(**data)
224+
elif chart_type == ChartType.PIE:
225+
chart = PieChart(**data)
226+
elif chart_type == ChartType.BOX_AND_WHISKER:
227+
chart = BoxAndWhiskerChart(**data)
228+
elif chart_type == ChartType.SUPERCHART:
229+
chart = SuperChart(**data)
230+
else:
231+
chart = Chart(**data)
232+
233+
return chart

0 commit comments

Comments
 (0)