import React, { useState } from "react" import { WordChip } from "./WordChip" import { Spinner } from "./Spinner" import { Word } from "../interfaces" async function checkText(text: string): Promise<Word[]> { const encodedText = encodeURIComponent(text); const response = await fetch(`/check?text=${encodedText}`); const data = await response.json(); console.log(data); return data.words; } export default function App() { const [threshold, setThreshold] = useState(-5.0) const [context, setContext] = useState("") const [wordlist, setWordlist] = useState("") const [showWholePrompt, setShowWholePrompt] = useState(false) // const [text, setText] = useState("I just drove to the store to but eggs, but they had some.") const [text, setText] = useState("I drove to the stove to but eggs") const [mode, setMode] = useState<"edit" | "check">("edit") const [words, setWords] = useState<Word[]>([]) const [isLoading, setIsLoading] = useState(false) const check = async (text: string) => { setIsLoading(true) try { const checkedWords = await checkText(text) setWords(checkedWords) } finally { setIsLoading(false) setMode("check") } } const toggleMode = async () => { if (mode === "edit") { setIsLoading(true) await check(text) } else { setMode("edit") } } const handleReplace = async (index: number, newWord: string) => { console.log("handleReplace", index, newWord) const updatedWords = words.map((w, i) => { if (i === index) { return { text: newWord, logprob: 0, replacements: [] } } return w }) setWords(updatedWords) const newText = updatedWords.map(w => w.text).join("") setText(newText) await check(newText) } let result if (mode === "edit") { result = ( <div className="result-container"> {isLoading && <Spinner />} <textarea value={text} onChange={e => setText(e.target.value)} /> </div> ) } else { result = ( <div className="result-container"> {isLoading && <Spinner />} <div className="result"> {words.map((word, index) => ( <WordChip key={index} word={word.text} logprob={word.logprob} threshold={threshold} replacements={word.replacements} onReplace={(newWord) => handleReplace(index, newWord)} /> ))} </div> </div> ) } return ( <main> <h1>GPTed</h1> <details> <summary>Advanced settings</summary> <label> <strong>Threshold:</strong> <input type="number" step="1" value={threshold} onChange={e => setThreshold(Number(e.target.value))} /> <small> The <a href="https://en.wikipedia.org/wiki/Log_probability" target="_blank" rel="noreferrer">logprob</a> threshold. Tokens with logprobs smaller than this will be marked red. </small> </label> <label> <strong>Context:</strong> <small>Context for the text, which can help GPT3 better rank certain words.</small> <textarea placeholder="A short essay about picnics" value={context} onChange={e => setContext(e.target.value)} /> </label> <label> <strong>Dictionary:</strong> <small>Known words or phrases. Helpful for uncommon or invented words and names.</small> <textarea placeholder="jujubu eschaton Frodo Baggins" value={wordlist} onChange={e => setWordlist(e.target.value)} /> </label> <label> <strong>Show whole prompt:</strong> <input type="checkbox" checked={showWholePrompt} onChange={e => setShowWholePrompt(e.target.checked)} /> <small> Show the whole prompt in the token view, instead of just your text. Mostly useful for debugging or curiosity. </small> </label> </details> <section id="inner"> {result} <button onClick={toggleMode}> {mode === "edit" ? "Check" : "Edit"} </button> <p> <small> Based on <a href="https://github.com/vgel/gpted">GPTed</a> by <a href="https://vgel.me">Theia Vogel</a>. Made with React, Transformers, LLama 3.2, and transitively, most of the web. <br /> This software is provided with absolutely no warranty. </small> </p> </section> </main> ) }