Skip to content

Commit 36ca2c4

Browse files
committed
Cooler Python demo
1 parent 0ac7f20 commit 36ca2c4

3 files changed

Lines changed: 61 additions & 82 deletions

File tree

docs/app/(home)/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const demos = [
3333
{
3434
id: "execute-code",
3535
title: "Execute Code",
36-
description: "Run Python in the browser and display interactive chart artifacts inline.",
36+
description: "Run Python in the browser and display rich visualizations inline.",
3737
storyId: "chat-chatview--execute-code",
3838
},
3939
{

ui/public/story-assets/lorenz.png

600 KB
Loading

ui/src/components/ChatView/ChatView.stories.tsx

Lines changed: 60 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -833,84 +833,64 @@ function ExecuteCodeStory({ onSendMessage }: { onSendMessage: (content: string)
833833
role: "input",
834834
data: {
835835
language: "python",
836-
code: `import matplotlib.pyplot as plt
837-
import matplotlib.ticker as mticker
838-
839-
categories = ['Cloud', 'Enterprise', 'Consumer', 'Services', 'Hardware']
840-
q3_revenue = [1.42, 1.05, 0.82, 0.58, 0.33]
841-
q2_revenue = [1.18, 0.94, 0.79, 0.52, 0.31]
842-
843-
x = range(len(categories))
844-
width = 0.35
845-
846-
fig, ax = plt.subplots(figsize=(10, 6))
847-
bars_q2 = ax.bar([i - width/2 for i in x], q2_revenue, width, label='Q2', color='#94a3b8')
848-
bars_q3 = ax.bar([i + width/2 for i in x], q3_revenue, width, label='Q3', color='#3b82f6')
849-
850-
ax.set_ylabel('Revenue ($B)')
851-
ax.set_title('Revenue by Segment: Q2 vs Q3 2025')
852-
ax.set_xticks(x)
853-
ax.set_xticklabels(categories)
854-
ax.yaxis.set_major_formatter(mticker.FormatStrFormatter('$%.2f'))
855-
ax.legend()
856-
ax.bar_label(bars_q3, fmt='$%.2f', padding=3, fontsize=9)
836+
code: `import numpy as np
837+
import matplotlib.pyplot as plt
838+
from matplotlib.collections import LineCollection
839+
840+
# Simulate the Lorenz system
841+
sigma, rho, beta = 10, 28, 8/3
842+
dt = 0.002
843+
steps = 20000
844+
845+
xyz = np.empty((steps, 3))
846+
xyz[0] = [0.1, 0, 0]
847+
for i in range(1, steps):
848+
x, y, z = xyz[i-1]
849+
xyz[i] = xyz[i-1] + dt * np.array([
850+
sigma * (y - x),
851+
x * (rho - z) - y,
852+
x * y - beta * z,
853+
])
854+
855+
fig, ax = plt.subplots(figsize=(10, 7), facecolor='#0f172a')
856+
ax.set_facecolor('#0f172a')
857+
858+
# Color by velocity (speed of change)
859+
velocity = np.linalg.norm(np.diff(xyz, axis=0), axis=1)
860+
points = xyz[:-1, [0, 2]] # project onto x-z plane
861+
segments = np.stack([points[:-1], points[1:]], axis=1)
862+
863+
lc = LineCollection(segments, cmap='turbo', linewidths=0.6, alpha=0.9)
864+
lc.set_array(velocity[:-1])
865+
ax.add_collection(lc)
866+
ax.autoscale()
867+
ax.set_xlabel('x', color='#94a3b8')
868+
ax.set_ylabel('z', color='#94a3b8')
869+
ax.set_title('Lorenz Attractor — Colored by Velocity', color='white', fontsize=14)
870+
ax.tick_params(colors='#475569')
871+
for spine in ax.spines.values():
872+
spine.set_color('#1e293b')
857873
plt.tight_layout()
858-
plt.savefig('revenue_comparison.png', dpi=150)
859-
print("Chart saved successfully")`,
874+
plt.savefig('lorenz.png', dpi=180, facecolor='#0f172a')
875+
print(f"Rendered {steps:,} steps, max velocity: {velocity.max():.1f}")`,
860876
},
861877
};
862878

863879
const chartOutputArtifact: Artifact = {
864880
id: "chart-output-1",
865-
type: "chart",
866-
title: "Revenue by Segment",
881+
type: "image",
882+
title: "Lorenz Attractor",
867883
role: "output",
868-
data: {
869-
spec: {
870-
$schema: "https://vega.github.io/schema/vega-lite/v6.json",
871-
title: "Revenue by Segment: Q2 vs Q3 2025",
872-
width: 500,
873-
height: 300,
874-
data: {
875-
values: [
876-
{ segment: "Cloud", quarter: "Q2", revenue: 1.18 },
877-
{ segment: "Cloud", quarter: "Q3", revenue: 1.42 },
878-
{ segment: "Enterprise", quarter: "Q2", revenue: 0.94 },
879-
{ segment: "Enterprise", quarter: "Q3", revenue: 1.05 },
880-
{ segment: "Consumer", quarter: "Q2", revenue: 0.79 },
881-
{ segment: "Consumer", quarter: "Q3", revenue: 0.82 },
882-
{ segment: "Services", quarter: "Q2", revenue: 0.52 },
883-
{ segment: "Services", quarter: "Q3", revenue: 0.58 },
884-
{ segment: "Hardware", quarter: "Q2", revenue: 0.31 },
885-
{ segment: "Hardware", quarter: "Q3", revenue: 0.33 },
886-
],
887-
},
888-
mark: "bar",
889-
encoding: {
890-
x: { field: "segment", type: "nominal", title: "Segment", axis: { labelAngle: 0 } },
891-
xOffset: { field: "quarter" },
892-
y: {
893-
field: "revenue",
894-
type: "quantitative",
895-
title: "Revenue ($B)",
896-
},
897-
color: {
898-
field: "quarter",
899-
type: "nominal",
900-
scale: { range: ["#94a3b8", "#3b82f6"] },
901-
title: "Quarter",
902-
},
903-
},
904-
},
905-
},
884+
mimeType: "image/png",
885+
data: { src: "/story-assets/lorenz.png" },
906886
};
907887

908888
const stdoutArtifact: Artifact = {
909889
id: "py-stdout-1",
910890
type: "code",
911891
title: "stdout",
912892
role: "output",
913-
data: { language: "text", code: "Chart saved successfully" },
893+
data: { language: "text", code: "Rendered 20,000 steps, max velocity: 46.3" },
914894
};
915895

916896
const displaySelectionArtifact: Artifact = {
@@ -966,31 +946,30 @@ print("Chart saved successfully")`,
966946
{
967947
id: "py-1",
968948
role: "user",
969-
content: "Can you visualize Q2 vs Q3 revenue by business segment?",
949+
content:
950+
"Simulate the Lorenz attractor and visualize the chaotic trajectory colored by velocity.",
970951
timestamp: new Date("2025-10-20T15:10:00"),
971952
},
972953
{
973954
id: "py-2",
974955
role: "assistant",
975956
model: "openai/gpt-5.3",
976-
content: `I created a grouped bar chart comparing Q2 and Q3 revenue across all five business segments.
977-
978-
**Key highlights:**
979-
- **Cloud** led growth with **$1.42B** in Q3, up 20% from Q2
980-
- **Enterprise** crossed $1B for the first time at **$1.05B**
981-
- **Consumer** and **Services** showed steady single-digit growth
982-
- **Hardware** remained stable at **$0.33B**
957+
content: `Here's the Lorenz attractor projected onto the x-z plane, with each point colored by its instantaneous velocity through phase space.
983958
984-
Total Q3 revenue of **$4.20B** represents a **12.3%** increase over Q2's $3.74B.`,
959+
**What you're seeing:**
960+
- The system traces two lobes — the classic "butterfly" shape of deterministic chaos
961+
- **High-velocity regions** (bright yellow) occur during transitions between lobes, where the trajectory is flung across the attractor
962+
- **Low-velocity regions** (deep purple) mark the tight spirals where the system lingers before switching
963+
- Despite being fully deterministic (\u03C3=10, \u03C1=28, \u03B2=8/3), the trajectory never repeats — a hallmark of strange attractors`,
985964
timestamp: new Date("2025-10-20T15:10:12"),
986965
usage: {
987-
inputTokens: 85,
988-
outputTokens: 310,
989-
totalTokens: 395,
990-
cost: 0.0066,
966+
inputTokens: 92,
967+
outputTokens: 380,
968+
totalTokens: 472,
969+
cost: 0.0079,
991970
firstTokenMs: 3800,
992971
totalDurationMs: 7200,
993-
tokensPerSecond: 43.1,
972+
tokensPerSecond: 52.8,
994973
},
995974
artifacts: [chartOutputArtifact, displaySelectionArtifact],
996975
toolExecutionRounds: pythonRounds,
@@ -1011,11 +990,11 @@ export const ExecuteCode: Story = {
1011990
const canvas = within(canvasElement);
1012991

1013992
// Verify user message
1014-
await expect(canvas.getByText(/Q2 vs Q3 revenue/)).toBeInTheDocument();
993+
await expect(canvas.getByText(/Simulate the Lorenz attractor/)).toBeInTheDocument();
1015994

1016995
// Verify assistant content has key data points
1017-
await expect(canvas.getByText(/\$1\.42B/)).toBeInTheDocument();
1018-
await expect(canvas.getByText(/12\.3%/)).toBeInTheDocument();
996+
await expect(canvas.getByText(/butterfly/)).toBeInTheDocument();
997+
await expect(canvas.getByText(/strange attractors/)).toBeInTheDocument();
1019998

1020999
// Verify tool execution block is rendered (2 tools across 2 rounds)
10211000
await expect(canvas.getByText(/2 tools/)).toBeInTheDocument();

0 commit comments

Comments
 (0)