@@ -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
1919You'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
4344Replace ` 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
5152interface 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
140158Question: "${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
181210Download a sample CSV:
@@ -206,26 +235,37 @@ Response:
206235
207236``` json
208237{
209- "success" : true ,
210- "output" : " Average ratings by year:\n 2020: 8.5\n 2021: 7.2\n 2022: 9.1" ,
211- "chart" : " data:image/png;base64,..." ,
212- "code" : " import pandas as pd\n import matplotlib.pyplot as plt\n ..."
238+ "success" : true ,
239+ "output" : " Average ratings by year:\n 2020: 8.5\n 2021: 7.2\n 2022: 9.1" ,
240+ "chart" : " data:image/png;base64,..." ,
241+ "code" : " import pandas as pd\n import matplotlib.pyplot as plt\n ..."
213242}
214243```
215244
216245## 6. Deploy
217246
247+ Deploy your Worker:
248+
218249``` sh
219250npx 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
223262Wait 2-3 minutes after first deployment for container provisioning.
224263:::
225264
226265## What you built
227266
228267An 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