File size: 4,045 Bytes
8e0957b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useEffect, useState } from 'react';
import { CONFIG } from '../config';
import { getPromptGeneratePodcastScript } from '../utils/prompts';
import { getSSEStreamAsync } from '../utils/utils';

interface SplitContent {
  thought: string;
  codeBlock: string;
}

const getFromTo = (content: string, from: string, to: string): string => {
  const firstSplit = content.split(from, 2);
  if (firstSplit[1] !== undefined) {
    const secondSplit = firstSplit[1].split(to, 1);
    return secondSplit[0];
  } else {
    return '';
  }
};

const splitContent = (content: string): SplitContent => {
  return {
    thought: getFromTo(content, '<think>', '</think>').trim(),
    codeBlock: getFromTo(content, '```yaml', '```').trim(),
  };
};

export const ScriptMaker = ({
  setScript,
  setBusy,
  busy,
}: {
  setScript: (script: string) => void;
  setBusy: (busy: boolean) => void;
  busy: boolean;
}) => {
  const [input, setInput] = useState<string>('');
  const [note, setNote] = useState<string>('');
  const [thought, setThought] = useState<string>('');
  const [isGenerating, setIsGenerating] = useState<boolean>(false);

  useEffect(() => {
    setBusy(isGenerating);
  }, [isGenerating]);

  const generate = async () => {
    setIsGenerating(true);
    setThought('');
    try {
      let responseContent = '';
      const fetchResponse = await fetch(CONFIG.llmEndpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          messages: [
            {
              role: 'user',
              content: getPromptGeneratePodcastScript(input, note),
            },
          ],
          temperature: 0.3,
          stream: true,
        }),
      });
      if (fetchResponse.status !== 200) {
        const body = await fetchResponse.json();
        throw new Error(body?.error?.message || 'Unknown error');
      }
      const chunks = getSSEStreamAsync(fetchResponse);
      for await (const chunk of chunks) {
        // const stop = chunk.stop;
        if (chunk.error) {
          throw new Error(chunk.error?.message || 'Unknown error');
        }
        const addedContent = chunk.choices[0].delta.content;
        responseContent += addedContent;
        const { thought, codeBlock } = splitContent(responseContent);
        setThought(thought);
        if (codeBlock.length > 0) {
          setScript(codeBlock);
        }
      }
    } catch (error) {
      console.error(error);
      alert('Failed to generate the script. Please try again.');
    }
    setIsGenerating(false);
  };

  return (
    <div className="card bg-base-100 w-full shadow-xl">
      <div className="card-body">
        <h2 className="card-title">Step 1: Input information</h2>
        <textarea
          className="textarea textarea-bordered w-full h-72 p-2"
          placeholder="Type your input information here (an article, a document, etc)..."
          value={input}
          onChange={(e) => setInput(e.target.value)}
          disabled={isGenerating || busy}
        ></textarea>
        <textarea
          className="textarea textarea-bordered w-full h-24 p-2"
          placeholder="Optional note (the theme, tone, etc)..."
          value={note}
          onChange={(e) => setNote(e.target.value)}
          disabled={isGenerating || busy}
        ></textarea>

        {thought.length > 0 && (
          <>
            <p>Thought process:</p>
            <textarea
              className="textarea textarea-bordered w-full h-24 p-2"
              value={thought}
              readOnly
            ></textarea>
          </>
        )}
        <button
          className="btn btn-primary mt-2"
          onClick={generate}
          disabled={isGenerating || busy}
        >
          {isGenerating ? (
            <>
              <span className="loading loading-spinner loading-sm"></span>
              Generating...
            </>
          ) : (
            'Generate script'
          )}
        </button>
      </div>
    </div>
  );
};