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