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 |
)}
|