|
<script lang="ts"> |
|
import type { GradioClient, TextGenerationParams, TextGenerationResult } from '$lib/types'; |
|
|
|
interface Props { |
|
client: GradioClient | null; |
|
} |
|
|
|
let { client = null }: Props = $props(); |
|
|
|
let params: TextGenerationParams = $state({ |
|
prompt: "", |
|
maxTokens: 200, |
|
temperature: 1.0, |
|
topP: 0.7, |
|
presencePenalty: 0.1, |
|
countPenalty: 0.1 |
|
}); |
|
|
|
let isGenerating = $state(false); |
|
let result: TextGenerationResult | null = $state(null); |
|
let error: string | null = $state(null); |
|
|
|
async function handleSubmit(e: Event) { |
|
e.preventDefault(); |
|
|
|
if (!client || !params.prompt.trim()) { |
|
error = "Please enter a prompt."; |
|
return; |
|
} |
|
|
|
isGenerating = true; |
|
error = null; |
|
result = null; |
|
|
|
try { |
|
const output = await client.predict(0, [ |
|
params.prompt, |
|
params.maxTokens, |
|
params.temperature, |
|
params.topP, |
|
params.presencePenalty, |
|
params.countPenalty |
|
]); |
|
|
|
const generatedText = output.data[0]; |
|
|
|
result = { |
|
text: generatedText, |
|
prompt: params.prompt |
|
}; |
|
} catch (err) { |
|
console.error(err); |
|
error = `Text generation failed: ${err}`; |
|
} finally { |
|
isGenerating = false; |
|
} |
|
} |
|
</script> |
|
|
|
<form class="text-form" onsubmit={handleSubmit}> |
|
<h3>Generate Text with RWKV</h3> |
|
|
|
<label for="textPrompt">Prompt</label> |
|
<textarea |
|
id="textPrompt" |
|
bind:value={params.prompt} |
|
rows="4" |
|
placeholder="Enter your prompt here..." |
|
disabled={isGenerating} |
|
></textarea> |
|
|
|
<div class="input-row"> |
|
<div class="input-group"> |
|
<label for="maxTokens">Max Tokens</label> |
|
<input |
|
type="number" |
|
id="maxTokens" |
|
bind:value={params.maxTokens} |
|
min="10" |
|
max="1000" |
|
step="10" |
|
disabled={isGenerating} |
|
/> |
|
</div> |
|
<div class="input-group"> |
|
<label for="temperature">Temperature</label> |
|
<input |
|
type="number" |
|
id="temperature" |
|
bind:value={params.temperature} |
|
min="0.2" |
|
max="2.0" |
|
step="0.1" |
|
disabled={isGenerating} |
|
/> |
|
</div> |
|
</div> |
|
|
|
<div class="input-row"> |
|
<div class="input-group"> |
|
<label for="topP">Top P</label> |
|
<input |
|
type="number" |
|
id="topP" |
|
bind:value={params.topP} |
|
min="0.0" |
|
max="1.0" |
|
step="0.05" |
|
disabled={isGenerating} |
|
/> |
|
</div> |
|
<div class="input-group"> |
|
<label for="presencePenalty">Presence Penalty</label> |
|
<input |
|
type="number" |
|
id="presencePenalty" |
|
bind:value={params.presencePenalty} |
|
min="0.0" |
|
max="1.0" |
|
step="0.1" |
|
disabled={isGenerating} |
|
/> |
|
</div> |
|
</div> |
|
|
|
<label for="countPenalty">Count Penalty</label> |
|
<input |
|
type="number" |
|
id="countPenalty" |
|
bind:value={params.countPenalty} |
|
min="0.0" |
|
max="1.0" |
|
step="0.1" |
|
disabled={isGenerating} |
|
/> |
|
|
|
<button |
|
type="submit" |
|
class="generate-button" |
|
disabled={isGenerating || !client} |
|
> |
|
{isGenerating ? 'Generating Text…' : 'Generate Text'} |
|
</button> |
|
</form> |
|
|
|
{#if error} |
|
<div class="error-message">{error}</div> |
|
{/if} |
|
|
|
{#if result} |
|
<div class="text-result"> |
|
<h4>Generated Text</h4> |
|
<p><strong>Prompt:</strong> {result.prompt.substring(0, 100)}{result.prompt.length > 100 ? '...' : ''}</p> |
|
<p><strong>Generated:</strong></p> |
|
<div class="generated-text">{result.text}</div> |
|
</div> |
|
{/if} |
|
|
|
<style> |
|
.text-form { |
|
margin-top: 2rem; |
|
padding-top: 2rem; |
|
border-top: 1px solid #eee; |
|
} |
|
|
|
h3 { |
|
margin-top: 0; |
|
margin-bottom: 1.5rem; |
|
} |
|
|
|
label { |
|
font-weight: 600; |
|
margin-bottom: 0.25rem; |
|
display: block; |
|
} |
|
|
|
textarea { |
|
width: 100%; |
|
padding: 0.5rem; |
|
border: 1px solid #ccc; |
|
border-radius: 4px; |
|
box-sizing: border-box; |
|
margin-bottom: 1rem; |
|
font-family: inherit; |
|
resize: vertical; |
|
} |
|
|
|
input[type="number"] { |
|
width: 100%; |
|
padding: 0.5rem 0.75rem; |
|
border: 1px solid #ccc; |
|
border-radius: 4px; |
|
box-sizing: border-box; |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.input-row { |
|
display: flex; |
|
gap: 1rem; |
|
} |
|
|
|
.input-group { |
|
flex: 1; |
|
} |
|
|
|
.generate-button { |
|
background: #007bff; |
|
color: #fff; |
|
border: none; |
|
padding: 0.6rem 1.4rem; |
|
border-radius: 6px; |
|
cursor: pointer; |
|
font-size: 1rem; |
|
transition: background-color 0.2s; |
|
} |
|
|
|
.generate-button:hover:not(:disabled) { |
|
background: #0056b3; |
|
} |
|
|
|
.generate-button:disabled { |
|
background: #9ac7ff; |
|
cursor: not-allowed; |
|
} |
|
|
|
.text-result { |
|
background: #f8f9fa; |
|
padding: 1rem; |
|
border-radius: 6px; |
|
margin-top: 1rem; |
|
} |
|
|
|
.text-result h4 { |
|
margin-top: 0; |
|
} |
|
|
|
.generated-text { |
|
white-space: pre-wrap; |
|
font-family: monospace; |
|
background: #fff; |
|
padding: 1rem; |
|
border-radius: 4px; |
|
border: 1px solid #ddd; |
|
} |
|
|
|
.error-message { |
|
color: #dc3545; |
|
margin-top: 1rem; |
|
padding: 0.5rem; |
|
background: #f8d7da; |
|
border-radius: 4px; |
|
} |
|
</style> |