Update app.py
Browse files
app.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
import React, { useState } from 'react';
|
2 |
-
import { ChevronRight, Play, Download, Users, Clock, DollarSign, CheckCircle, Loader, Zap, BookOpen, Code, Mic, AlertCircle } from 'lucide-react';
|
3 |
|
4 |
const WorkshopGenerator = () => {
|
5 |
-
const [currentStep, setCurrentStep] = useState('
|
|
|
6 |
const [workshopData, setWorkshopData] = useState({
|
7 |
topic: '',
|
8 |
audience: '',
|
@@ -21,14 +22,21 @@ const WorkshopGenerator = () => {
|
|
21 |
{ id: 'code', name: 'Code Agent', icon: Code, description: 'Creating hands-on exercises' }
|
22 |
];
|
23 |
|
24 |
-
// Real Claude API call function
|
25 |
const callClaudeAPI = async (prompt, maxTokens = 2000) => {
|
26 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
28 |
method: "POST",
|
29 |
-
headers:
|
30 |
-
"Content-Type": "application/json",
|
31 |
-
},
|
32 |
body: JSON.stringify({
|
33 |
model: "claude-sonnet-4-20250514",
|
34 |
max_tokens: maxTokens,
|
@@ -39,7 +47,8 @@ const WorkshopGenerator = () => {
|
|
39 |
});
|
40 |
|
41 |
if (!response.ok) {
|
42 |
-
|
|
|
43 |
}
|
44 |
|
45 |
const data = await response.json();
|
@@ -210,6 +219,29 @@ Focus on practical, business-relevant examples. DO NOT include any text outside
|
|
210 |
return await callClaudeAPI(prompt, 3000);
|
211 |
};
|
212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
const generateWorkshop = async () => {
|
214 |
setIsGenerating(true);
|
215 |
setCurrentStep('generating');
|
@@ -227,7 +259,6 @@ Focus on practical, business-relevant examples. DO NOT include any text outside
|
|
227 |
|
228 |
let topicData;
|
229 |
try {
|
230 |
-
// Clean the response to extract JSON
|
231 |
const cleanResponse = topicResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
232 |
topicData = JSON.parse(cleanResponse);
|
233 |
} catch (parseError) {
|
@@ -336,7 +367,8 @@ Focus on practical, business-relevant examples. DO NOT include any text outside
|
|
336 |
...generatedContent,
|
337 |
generated_by: "AI Workshop in a Box",
|
338 |
export_date: new Date().toISOString(),
|
339 |
-
workshop_config: workshopData
|
|
|
340 |
};
|
341 |
|
342 |
const dataStr = JSON.stringify(workshopPackage, null, 2);
|
@@ -366,7 +398,7 @@ Focus on practical, business-relevant examples. DO NOT include any text outside
|
|
366 |
</div>
|
367 |
<div>
|
368 |
<h1 className="text-2xl font-bold">AI Workshop in a Box</h1>
|
369 |
-
<p className="text-blue-200 text-sm">
|
370 |
</div>
|
371 |
</div>
|
372 |
<div className="flex items-center space-x-4">
|
@@ -390,6 +422,56 @@ Focus on practical, business-relevant examples. DO NOT include any text outside
|
|
390 |
</div>
|
391 |
)}
|
392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
393 |
{/* Step 1: Input */}
|
394 |
{currentStep === 'input' && (
|
395 |
<div className="max-w-2xl mx-auto">
|
@@ -463,6 +545,16 @@ Focus on practical, business-relevant examples. DO NOT include any text outside
|
|
463 |
<ChevronRight className="w-5 h-5" />
|
464 |
</button>
|
465 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
466 |
</div>
|
467 |
</div>
|
468 |
)}
|
|
|
1 |
import React, { useState } from 'react';
|
2 |
+
import { ChevronRight, Play, Download, Users, Clock, DollarSign, CheckCircle, Loader, Zap, BookOpen, Code, Mic, AlertCircle, Settings } from 'lucide-react';
|
3 |
|
4 |
const WorkshopGenerator = () => {
|
5 |
+
const [currentStep, setCurrentStep] = useState('setup');
|
6 |
+
const [apiKey, setApiKey] = useState('');
|
7 |
const [workshopData, setWorkshopData] = useState({
|
8 |
topic: '',
|
9 |
audience: '',
|
|
|
22 |
{ id: 'code', name: 'Code Agent', icon: Code, description: 'Creating hands-on exercises' }
|
23 |
];
|
24 |
|
25 |
+
// Real Claude API call function with proper authentication
|
26 |
const callClaudeAPI = async (prompt, maxTokens = 2000) => {
|
27 |
try {
|
28 |
+
const headers = {
|
29 |
+
"Content-Type": "application/json",
|
30 |
+
};
|
31 |
+
|
32 |
+
// Add API key for external deployment, or use built-in access for Claude.ai
|
33 |
+
if (apiKey && apiKey.startsWith('sk-ant-')) {
|
34 |
+
headers["x-api-key"] = apiKey;
|
35 |
+
}
|
36 |
+
|
37 |
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
38 |
method: "POST",
|
39 |
+
headers: headers,
|
|
|
|
|
40 |
body: JSON.stringify({
|
41 |
model: "claude-sonnet-4-20250514",
|
42 |
max_tokens: maxTokens,
|
|
|
47 |
});
|
48 |
|
49 |
if (!response.ok) {
|
50 |
+
const errorText = await response.text();
|
51 |
+
throw new Error(`API request failed: ${response.status} - ${errorText}`);
|
52 |
}
|
53 |
|
54 |
const data = await response.json();
|
|
|
219 |
return await callClaudeAPI(prompt, 3000);
|
220 |
};
|
221 |
|
222 |
+
const testAPIConnection = async () => {
|
223 |
+
try {
|
224 |
+
const testPrompt = "Respond with exactly: API_TEST_SUCCESS";
|
225 |
+
const response = await callClaudeAPI(testPrompt, 50);
|
226 |
+
return response.includes("API_TEST_SUCCESS");
|
227 |
+
} catch (error) {
|
228 |
+
console.error("API test failed:", error);
|
229 |
+
return false;
|
230 |
+
}
|
231 |
+
};
|
232 |
+
|
233 |
+
const handleAPISetup = async () => {
|
234 |
+
setError('');
|
235 |
+
|
236 |
+
// Test API connection
|
237 |
+
const isConnected = await testAPIConnection();
|
238 |
+
if (isConnected) {
|
239 |
+
setCurrentStep('input');
|
240 |
+
} else {
|
241 |
+
setError('API connection failed. Please check your API key or try again.');
|
242 |
+
}
|
243 |
+
};
|
244 |
+
|
245 |
const generateWorkshop = async () => {
|
246 |
setIsGenerating(true);
|
247 |
setCurrentStep('generating');
|
|
|
259 |
|
260 |
let topicData;
|
261 |
try {
|
|
|
262 |
const cleanResponse = topicResponse.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
|
263 |
topicData = JSON.parse(cleanResponse);
|
264 |
} catch (parseError) {
|
|
|
367 |
...generatedContent,
|
368 |
generated_by: "AI Workshop in a Box",
|
369 |
export_date: new Date().toISOString(),
|
370 |
+
workshop_config: workshopData,
|
371 |
+
api_version: "claude-sonnet-4-20250514"
|
372 |
};
|
373 |
|
374 |
const dataStr = JSON.stringify(workshopPackage, null, 2);
|
|
|
398 |
</div>
|
399 |
<div>
|
400 |
<h1 className="text-2xl font-bold">AI Workshop in a Box</h1>
|
401 |
+
<p className="text-blue-200 text-sm">Production Multi-Agent Generator</p>
|
402 |
</div>
|
403 |
</div>
|
404 |
<div className="flex items-center space-x-4">
|
|
|
422 |
</div>
|
423 |
)}
|
424 |
|
425 |
+
{/* Step 0: API Setup */}
|
426 |
+
{currentStep === 'setup' && (
|
427 |
+
<div className="max-w-2xl mx-auto">
|
428 |
+
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 border border-white/20">
|
429 |
+
<div className="text-center mb-6">
|
430 |
+
<Settings className="w-16 h-16 text-cyan-400 mx-auto mb-4" />
|
431 |
+
<h2 className="text-3xl font-bold mb-2">API Configuration</h2>
|
432 |
+
<p className="text-blue-200">Set up your Claude API access to start generating workshops</p>
|
433 |
+
</div>
|
434 |
+
|
435 |
+
<div className="space-y-6">
|
436 |
+
<div>
|
437 |
+
<label className="block text-sm font-medium mb-2">
|
438 |
+
Claude API Key (Optional for Claude.ai)
|
439 |
+
</label>
|
440 |
+
<input
|
441 |
+
type="password"
|
442 |
+
placeholder="sk-ant-api03-... (leave blank if running in Claude.ai)"
|
443 |
+
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg focus:ring-2 focus:ring-cyan-400 focus:border-transparent text-white placeholder-gray-300"
|
444 |
+
value={apiKey}
|
445 |
+
onChange={(e) => setApiKey(e.target.value)}
|
446 |
+
/>
|
447 |
+
<p className="text-xs text-gray-400 mt-2">
|
448 |
+
• Leave blank when running in Claude.ai (uses built-in access)<br/>
|
449 |
+
• Required when deploying to your own domain<br/>
|
450 |
+
• Get your API key from: <span className="text-cyan-400">console.anthropic.com</span>
|
451 |
+
</p>
|
452 |
+
</div>
|
453 |
+
|
454 |
+
<div className="bg-blue-500/20 border border-blue-400/30 rounded-lg p-4">
|
455 |
+
<h4 className="font-semibold text-blue-200 mb-2">Deployment Options:</h4>
|
456 |
+
<div className="text-sm text-blue-100 space-y-1">
|
457 |
+
<div>• <strong>Claude.ai:</strong> No API key needed (current environment)</div>
|
458 |
+
<div>• <strong>Your Domain:</strong> Add API key to environment variables</div>
|
459 |
+
<div>• <strong>GitHub:</strong> Store API key in repository secrets</div>
|
460 |
+
</div>
|
461 |
+
</div>
|
462 |
+
|
463 |
+
<button
|
464 |
+
onClick={handleAPISetup}
|
465 |
+
className="w-full py-4 bg-gradient-to-r from-cyan-500 to-blue-600 rounded-lg font-semibold text-lg hover:from-cyan-600 hover:to-blue-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
466 |
+
>
|
467 |
+
<CheckCircle className="w-5 h-5" />
|
468 |
+
<span>Test Connection & Continue</span>
|
469 |
+
</button>
|
470 |
+
</div>
|
471 |
+
</div>
|
472 |
+
</div>
|
473 |
+
)}
|
474 |
+
|
475 |
{/* Step 1: Input */}
|
476 |
{currentStep === 'input' && (
|
477 |
<div className="max-w-2xl mx-auto">
|
|
|
545 |
<ChevronRight className="w-5 h-5" />
|
546 |
</button>
|
547 |
</div>
|
548 |
+
|
549 |
+
<div className="mt-6 pt-6 border-t border-white/10">
|
550 |
+
<button
|
551 |
+
onClick={() => setCurrentStep('setup')}
|
552 |
+
className="text-gray-300 hover:text-white text-sm flex items-center space-x-2"
|
553 |
+
>
|
554 |
+
<Settings className="w-4 h-4" />
|
555 |
+
<span>Change API Settings</span>
|
556 |
+
</button>
|
557 |
+
</div>
|
558 |
</div>
|
559 |
</div>
|
560 |
)}
|