File size: 5,275 Bytes
7211e95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import React, { useState, useCallback } from 'react';
import { Zap } from 'lucide-react';

// Import sub-components for each step
import Header from './Header';
import SetupScreen from './SetupScreen';
import InputScreen from './InputScreen';
import GeneratingScreen from './GeneratingScreen';
import PreviewScreen from './PreviewScreen';

// Import the API services
import { callAnthropicAPI } from '../services/anthropic';
import { callHuggingFaceAPI } from '../services/huggingface'; // New service
import { getPrompts } from '../services/prompts';

const WorkshopGenerator = () => {
  const [currentStep, setCurrentStep] = useState('setup');
  const [apiKey, setApiKey] = useState({ anthropic: '', huggingface: '' });
  const [apiProvider, setApiProvider] = useState('anthropic'); // 'anthropic' or 'huggingface'
  const [workshopData, setWorkshopData] = useState({
    topic: '',
    audience: '',
    duration: '90',
    difficulty: 'intermediate'
  });
  const [generatedContent, setGeneratedContent] = useState(null);
  const [isGenerating, setIsGenerating] = useState(false);
  const [activeAgent, setActiveAgent] = useState('');
  const [error, setError] = useState('');

  const getApiCallFunction = useCallback(() => {
    if (apiProvider === 'huggingface') {
      return (prompt, maxTokens) => callHuggingFaceAPI(prompt, apiKey.huggingface, maxTokens);
    }
    // Default to Anthropic
    return (prompt, maxTokens) => callAnthropicAPI(prompt, apiKey.anthropic, maxTokens);
  }, [apiProvider, apiKey]);
  
  const generateWorkshop = async () => {
    setIsGenerating(true);
    setCurrentStep('generating');
    setError('');
    const callAPI = getApiCallFunction();
    const prompts = getPrompts(workshopData);

    try {
      const runAgent = async (agentName, prompt, maxTokens) => {
        setActiveAgent(agentName);
        const response = await callAPI(prompt, maxTokens);
        const cleanResponse = response.replace(/```json\n?/, "").replace(/```\n?/, "").trim();
        try {
          return JSON.parse(cleanResponse);
        } catch (parseError) {
          console.error(`Error parsing JSON from ${agentName}:`, parseError, "Raw response:", response);
          throw new Error(`Agent "${agentName}" returned invalid data. Please try again.`);
        }
      };

      // Run the agent pipeline
      const topicData = await runAgent('topic', prompts.topic, 1500);
      const contentData = await runAgent('content', prompts.content(JSON.stringify(topicData)), 3000);
      const slideData = await runAgent('slide', prompts.slide(JSON.stringify(contentData)), 4000);
      const codeData = await runAgent('code', prompts.code(JSON.stringify(topicData)), 3000);

      // Combine results
      setGeneratedContent({
        topic: topicData,
        content: contentData,
        slides: slideData,
        code: codeData,
        metadata: {
          title: topicData.title || workshopData.topic,
          modules: topicData.modules?.length || 0,
          slideCount: slideData.slide_count || slideData.slides?.length || 0,
          exercises: codeData.exercises?.length || 0,
          generatedAt: new Date().toISOString(),
          provider: apiProvider
        }
      });
      
      setCurrentStep('preview');
    } catch (err) {
      console.error("Workshop generation error:", err);
      setError(err.message);
      setCurrentStep('input');
    } finally {
      setIsGenerating(false);
      setActiveAgent('');
    }
  };

  const resetGenerator = () => {
    setCurrentStep('input');
    setGeneratedContent(null);
    setWorkshopData({ topic: '', audience: '', duration: '90', difficulty: 'intermediate' });
    setError('');
  };

  const renderStep = () => {
    switch (currentStep) {
      case 'setup':
        return <SetupScreen 
          apiKey={apiKey} 
          setApiKey={setApiKey} 
          apiProvider={apiProvider}
          setApiProvider={setApiProvider}
          setCurrentStep={setCurrentStep} 
          setError={setError} 
          getApiCallFunction={getApiCallFunction}
        />;
      case 'input':
        return <InputScreen 
          workshopData={workshopData} 
          setWorkshopData={setWorkshopData}
          isGenerating={isGenerating}
          generateWorkshop={generateWorkshop}
          setCurrentStep={setCurrentStep}
        />;
      case 'generating':
        return <GeneratingScreen activeAgent={activeAgent} />;
      case 'preview':
        return <PreviewScreen
          generatedContent={generatedContent}
          workshopData={workshopData}
          resetGenerator={resetGenerator}
        />;
      default:
        return <SetupScreen />;
    }
  };

  return (
    <div className="min-h-screen bg-gray-900 text-white font-sans">
        <Header />
        <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
            {error && (
                <div className="max-w-2xl mx-auto mb-6 bg-red-500/20 border border-red-400 rounded-lg p-4 flex items-center space-x-3">
                    <AlertCircle className="w-5 h-5 text-red-400" />
                    <span className="text-red-200">{error}</span>
                </div>
            )}
            {renderStep()}
        </main>
    </div>
  );
};

export default WorkshopGenerator;