File size: 4,367 Bytes
8214506
82b1b1d
 
 
 
 
0970991
82b1b1d
 
 
1fe47b1
82b1b1d
 
1fe47b1
 
 
 
 
82b1b1d
8214506
0970991
 
 
 
 
 
 
8214506
82b1b1d
8214506
 
 
1fe47b1
82b1b1d
 
0970991
8214506
82b1b1d
 
0970991
 
 
 
 
 
 
 
82b1b1d
 
 
8214506
 
1fe47b1
 
 
 
 
 
 
 
8214506
 
82b1b1d
0970991
 
 
 
 
 
8214506
82b1b1d
0970991
 
 
 
 
 
 
 
 
1fe47b1
 
0970991
 
 
82b1b1d
 
8214506
 
 
82b1b1d
8214506
 
 
 
 
82b1b1d
8214506
 
 
 
 
 
 
82b1b1d
8214506
 
 
 
82b1b1d
8214506
 
82b1b1d
8214506
 
 
 
 
 
 
 
82b1b1d
 
8214506
 
 
 
 
 
 
 
 
 
 
82b1b1d
8214506
 
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
import React, { useState } from "react"
import { TokenChip } from "./TokenChip"

interface Word {
  text: string
  logprob: number
  replacements: string[]
}

async function checkText(text: string): Promise<Word[]> {
  await new Promise(resolve => setTimeout(resolve, 1000));

  const words = text.split(/\b/)
  return words.map(word => ({
    text: word,
    logprob: -word.length,
    replacements: word.length < 4 ? [] : ["foo", "bar"]
  }))
}

// Add a new Spinner component
const Spinner = () => (
  <div className="spinner-overlay">
    <div className="spinner"></div>
  </div>
);

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("The cumbersomely quick brown fox jumps over the conspicuously lazy dog.")
  const [mode, setMode] = useState<"edit" | "check">("edit")
  const [words, setWords] = useState<Word[]>([])
  const [isLoading, setIsLoading] = useState(false)

  const toggleMode = async () => {
    if (mode === "edit") {
      setIsLoading(true)
      try {
        const checkedWords = await checkText(text)
        setWords(checkedWords)
      } finally {
        setMode("check")
        setIsLoading(false)
      }
    } else {
      setMode("edit")
    }
  }

  const handleReplace = (index: number, newToken: string) => {
    const updatedWords = [...words]
    updatedWords[index].text = newToken
    setWords(updatedWords)
    setText(updatedWords.map(w => w.text).join(""))
    setMode("edit")
  }

  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) => (
            <TokenChip
              key={index}
              token={word.text}
              logprob={word.logprob}
              threshold={threshold}
              replacements={word.replacements}
              onReplace={(newToken) => handleReplace(index, newToken)}
            />
          ))}
        </div>
      </div>
    )
  }

  return (
    <main>
      <h1>GPTed</h1>

      <details>
        <summary>Advanced settings</summary>
        <label>
          <strong>Threshold:</strong> <input type="number" step="0.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>
            Made by <a href="https://vgel.me">Theia Vogel</a> (<a href="https://twitter.com/voooooogel">@voooooogel</a>).
            Made with Svelte, GPT-3, and transitively, most of the web.
            <br />
            This software is provided with absolutely no warranty.
          </small>
        </p>
      </section>
    </main>
  )
}