Skip to content

Commit cac2af3

Browse files
committed
Fix tutorials
1 parent 0fd9335 commit cac2af3

File tree

4 files changed

+341
-138
lines changed

4 files changed

+341
-138
lines changed

src/content/docs/sandbox/tutorials/ai-code-executor.mdx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,15 @@ Return ONLY the code, no explanations.`
9898
return Response.json({ error: 'Failed to generate code' }, { status: 500 });
9999
}
100100

101+
// Strip markdown code fences if present
102+
const cleanCode = generatedCode
103+
.replace(/^```python?\n?/, '')
104+
.replace(/\n?```\s*$/, '')
105+
.trim();
106+
101107
// Execute the code in a sandbox
102108
const sandbox = getSandbox(env.Sandbox, 'demo-user');
103-
await sandbox.writeFile('/tmp/code.py', generatedCode);
109+
await sandbox.writeFile('/tmp/code.py', cleanCode);
104110
const result = await sandbox.exec('python /tmp/code.py');
105111

106112
return Response.json({
@@ -128,15 +134,19 @@ Return ONLY the code, no explanations.`
128134
4. Executes with `sandbox.exec('python /tmp/code.py')`
129135
5. Returns both the code and execution results
130136

131-
## 4. Set your Anthropic API key
137+
## 4. Set up local environment variables
132138

133-
Store your Anthropic API key as a secret:
139+
Create a `.dev.vars` file in your project root for local development:
134140

135141
```sh
136-
npx wrangler secret put ANTHROPIC_API_KEY
142+
echo "ANTHROPIC_API_KEY=your_api_key_here" > .dev.vars
137143
```
138144

139-
Paste your API key from the [Anthropic Console](https://console.anthropic.com/) when prompted.
145+
Replace `your_api_key_here` with your actual API key from the [Anthropic Console](https://console.anthropic.com/).
146+
147+
:::note
148+
The `.dev.vars` file is automatically gitignored and only used during local development with `npm run dev`.
149+
:::
140150

141151
## 5. Test locally
142152

@@ -178,6 +188,14 @@ Deploy your Worker:
178188
npx wrangler deploy
179189
```
180190

191+
Then set your Anthropic API key as a production secret:
192+
193+
```sh
194+
npx wrangler secret put ANTHROPIC_API_KEY
195+
```
196+
197+
Paste your API key from the [Anthropic Console](https://console.anthropic.com/) when prompted.
198+
181199
:::caution
182200
After first deployment, wait 2-3 minutes for container provisioning. Check status with `npx wrangler containers list`.
183201
:::

src/content/docs/sandbox/tutorials/analyze-data-with-ai.mdx

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Build an AI-powered data analysis system that accepts CSV uploads, uses Claude t
1717
<Render file="prereqs" product="workers" />
1818

1919
You'll also need:
20+
2021
- An [Anthropic API key](https://console.anthropic.com/) for Claude
2122
- [Docker](https://www.docker.com/) running locally
2223

@@ -43,10 +44,10 @@ cd analyze-data
4344
Replace `src/index.ts`:
4445

4546
```typescript
46-
import { getSandbox, proxyToSandbox, type Sandbox } from '@cloudflare/sandbox';
47-
import Anthropic from '@anthropic-ai/sdk';
47+
import { getSandbox, proxyToSandbox, type Sandbox } from "@cloudflare/sandbox";
48+
import Anthropic from "@anthropic-ai/sdk";
4849

49-
export { Sandbox } from '@cloudflare/sandbox';
50+
export { Sandbox } from "@cloudflare/sandbox";
5051

5152
interface Env {
5253
Sandbox: DurableObjectNamespace<Sandbox>;
@@ -58,48 +59,65 @@ export default {
5859
const proxyResponse = await proxyToSandbox(request, env);
5960
if (proxyResponse) return proxyResponse;
6061

61-
if (request.method !== 'POST') {
62-
return Response.json({ error: 'POST CSV file and question' }, { status: 405 });
62+
if (request.method !== "POST") {
63+
return Response.json(
64+
{ error: "POST CSV file and question" },
65+
{ status: 405 },
66+
);
6367
}
6468

6569
try {
6670
const formData = await request.formData();
67-
const csvFile = formData.get('file') as File;
68-
const question = formData.get('question') as string;
71+
const csvFile = formData.get("file") as File;
72+
const question = formData.get("question") as string;
6973

7074
if (!csvFile || !question) {
71-
return Response.json({ error: 'Missing file or question' }, { status: 400 });
75+
return Response.json(
76+
{ error: "Missing file or question" },
77+
{ status: 400 },
78+
);
7279
}
7380

7481
// Upload CSV to sandbox
7582
const sandbox = getSandbox(env.Sandbox, `analysis-${Date.now()}`);
76-
const csvPath = '/workspace/data.csv';
77-
await sandbox.writeFile(csvPath, new Uint8Array(await csvFile.arrayBuffer()));
83+
const csvPath = "/workspace/data.csv";
84+
await sandbox.writeFile(csvPath, await csvFile.text());
7885

7986
// Analyze CSV structure
8087
const structure = await sandbox.exec(
81-
`python -c "import pandas as pd; df = pd.read_csv('${csvPath}'); print(f'Rows: {len(df)}'); print(f'Columns: {list(df.columns)[:5]}')"`
88+
`python3 -c "import pandas as pd; df = pd.read_csv('${csvPath}'); print(f'Rows: {len(df)}'); print(f'Columns: {list(df.columns)[:5]}')"`,
8289
);
8390

8491
if (!structure.success) {
85-
return Response.json({ error: 'Failed to read CSV', details: structure.stderr }, { status: 400 });
92+
return Response.json(
93+
{ error: "Failed to read CSV", details: structure.stderr },
94+
{ status: 400 },
95+
);
8696
}
8797

8898
// Generate analysis code with Claude
89-
const code = await generateAnalysisCode(env.ANTHROPIC_API_KEY, csvPath, question, structure.stdout);
99+
const code = await generateAnalysisCode(
100+
env.ANTHROPIC_API_KEY,
101+
csvPath,
102+
question,
103+
structure.stdout,
104+
);
90105

91106
// Write and execute the analysis code
92-
await sandbox.writeFile('/workspace/analyze.py', code);
93-
const result = await sandbox.exec('python /workspace/analyze.py');
107+
await sandbox.writeFile("/workspace/analyze.py", code);
108+
const result = await sandbox.exec("python /workspace/analyze.py");
94109

95110
if (!result.success) {
96-
return Response.json({ error: 'Analysis failed', details: result.stderr }, { status: 500 });
111+
return Response.json(
112+
{ error: "Analysis failed", details: result.stderr },
113+
{ status: 500 },
114+
);
97115
}
98116

99117
// Check for generated chart
100118
let chart = null;
101119
try {
102-
const chartFile = await sandbox.readFile('/workspace/chart.png');
120+
const chartFile = await sandbox.readFile("/workspace/chart.png");
103121
const buffer = new Uint8Array(chartFile.content);
104122
chart = `data:image/png;base64,${btoa(String.fromCharCode(...buffer))}`;
105123
} catch {
@@ -112,9 +130,8 @@ export default {
112130
success: true,
113131
output: result.stdout,
114132
chart,
115-
code
133+
code,
116134
});
117-
118135
} catch (error: any) {
119136
return Response.json({ error: error.message }, { status: 500 });
120137
}
@@ -125,16 +142,17 @@ async function generateAnalysisCode(
125142
apiKey: string,
126143
csvPath: string,
127144
question: string,
128-
csvStructure: string
145+
csvStructure: string,
129146
): Promise<string> {
130147
const anthropic = new Anthropic({ apiKey });
131148

132149
const response = await anthropic.messages.create({
133-
model: 'claude-sonnet-4-5',
150+
model: "claude-sonnet-4-5",
134151
max_tokens: 2048,
135-
messages: [{
136-
role: 'user',
137-
content: `CSV at ${csvPath}:
152+
messages: [
153+
{
154+
role: "user",
155+
content: `CSV at ${csvPath}:
138156
${csvStructure}
139157
140158
Question: "${question}"
@@ -145,37 +163,48 @@ Generate Python code that:
145163
- Saves charts to /workspace/chart.png if helpful
146164
- Prints findings to stdout
147165
148-
Use pandas, numpy, matplotlib.`
149-
}],
150-
tools: [{
151-
name: 'generate_python_code',
152-
description: 'Generate Python code for data analysis',
153-
input_schema: {
154-
type: 'object',
155-
properties: {
156-
code: { type: 'string', description: 'Complete Python code' }
166+
Use pandas, numpy, matplotlib.`,
167+
},
168+
],
169+
tools: [
170+
{
171+
name: "generate_python_code",
172+
description: "Generate Python code for data analysis",
173+
input_schema: {
174+
type: "object",
175+
properties: {
176+
code: { type: "string", description: "Complete Python code" },
177+
},
178+
required: ["code"],
157179
},
158-
required: ['code']
159-
}
160-
}]
180+
},
181+
],
161182
});
162183

163184
for (const block of response.content) {
164-
if (block.type === 'tool_use' && block.name === 'generate_python_code') {
185+
if (block.type === "tool_use" && block.name === "generate_python_code") {
165186
return (block.input as { code: string }).code;
166187
}
167188
}
168189

169-
throw new Error('Failed to generate code');
190+
throw new Error("Failed to generate code");
170191
}
171192
```
172193

173-
## 4. Set your API key
194+
## 4. Set up local environment variables
195+
196+
Create a `.dev.vars` file in your project root for local development:
174197

175198
```sh
176-
npx wrangler secret put ANTHROPIC_API_KEY
199+
echo "ANTHROPIC_API_KEY=your_api_key_here" > .dev.vars
177200
```
178201

202+
Replace `your_api_key_here` with your actual API key from the [Anthropic Console](https://console.anthropic.com/).
203+
204+
:::note
205+
The `.dev.vars` file is automatically gitignored and only used during local development with `npm run dev`.
206+
:::
207+
179208
## 5. Test locally
180209

181210
Download a sample CSV:
@@ -206,26 +235,37 @@ Response:
206235

207236
```json
208237
{
209-
"success": true,
210-
"output": "Average ratings by year:\n2020: 8.5\n2021: 7.2\n2022: 9.1",
211-
"chart": "data:image/png;base64,...",
212-
"code": "import pandas as pd\nimport matplotlib.pyplot as plt\n..."
238+
"success": true,
239+
"output": "Average ratings by year:\n2020: 8.5\n2021: 7.2\n2022: 9.1",
240+
"chart": "data:image/png;base64,...",
241+
"code": "import pandas as pd\nimport matplotlib.pyplot as plt\n..."
213242
}
214243
```
215244

216245
## 6. Deploy
217246

247+
Deploy your Worker:
248+
218249
```sh
219250
npx wrangler deploy
220251
```
221252

253+
Then set your Anthropic API key as a production secret:
254+
255+
```sh
256+
npx wrangler secret put ANTHROPIC_API_KEY
257+
```
258+
259+
Paste your API key from the [Anthropic Console](https://console.anthropic.com/) when prompted.
260+
222261
:::caution
223262
Wait 2-3 minutes after first deployment for container provisioning.
224263
:::
225264

226265
## What you built
227266

228267
An AI data analysis system that:
268+
229269
- Uploads CSV files to sandboxes
230270
- Uses Claude's tool calling to generate analysis code
231271
- Executes Python with pandas and matplotlib

0 commit comments

Comments
 (0)