| import { useState } from 'react' |
|
|
| const MAX_TOKENS_LIMIT = 60 |
|
|
| const EXAMPLES = [ |
| 'العلم نور و', |
| 'أفضل طريقة لتعلم البرمجة هي', |
| 'في يوم من الأيام كان هناك', |
| 'الطقس اليوم', |
| ] |
|
|
| function App() { |
| const [prompt, setPrompt] = useState('') |
| const [maxTokens, setMaxTokens] = useState(20) |
| const [output, setOutput] = useState('') |
| const [loading, setLoading] = useState(false) |
| const [error, setError] = useState('') |
|
|
| async function handleGenerate() { |
| if (!prompt.trim()) { |
| setError('اكتب نصاً أولاً') |
| return |
| } |
| setLoading(true) |
| setError('') |
| setOutput('') |
| try { |
| const res = await fetch('/api/generate', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ prompt, max_new_tokens: maxTokens }), |
| }) |
| const data = await res.json() |
| if (!res.ok) { |
| throw new Error(data.detail || 'حدث خطأ غير متوقَّع') |
| } |
| setOutput(data.output) |
| } catch (err) { |
| setError(err.message) |
| } finally { |
| setLoading(false) |
| } |
| } |
|
|
| function handleKeyDown(e) { |
| if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { |
| handleGenerate() |
| } |
| } |
|
|
| return ( |
| <div className="page"> |
| <div className="card"> |
| <header> |
| <h1>HTDN</h1> |
| <p className="subtitle">نموذج لغوي عربي تجريبي (~350M معامل)</p> |
| </header> |
| |
| <label className="field-label" htmlFor="prompt"> |
| النص المبدئي — اكتب أي جملة تريد |
| </label> |
| <textarea |
| id="prompt" |
| value={prompt} |
| onChange={(e) => setPrompt(e.target.value)} |
| onKeyDown={handleKeyDown} |
| placeholder="اكتب جملة ليكملها النموذج... (Ctrl/Cmd + Enter للتوليد)" |
| rows={4} |
| /> |
| |
| <div className="examples"> |
| {EXAMPLES.map((ex) => ( |
| <button key={ex} className="example-chip" onClick={() => setPrompt(ex)}> |
| {ex} |
| </button> |
| ))} |
| </div> |
| |
| <div className="controls"> |
| <label htmlFor="tokens"> |
| عدد التوكِنات الجديدة: <strong>{maxTokens}</strong> |
| </label> |
| <input |
| id="tokens" |
| type="range" |
| min={1} |
| max={MAX_TOKENS_LIMIT} |
| value={maxTokens} |
| onChange={(e) => setMaxTokens(Number(e.target.value))} |
| /> |
| </div> |
| |
| <button className="generate-btn" onClick={handleGenerate} disabled={loading}> |
| {loading ? 'جارٍ التوليد...' : 'ولّد النص'} |
| </button> |
| |
| {error && <div className="error">{error}</div>} |
| |
| {output && ( |
| <div className="output"> |
| <h2>الناتج</h2> |
| <p>{output}</p> |
| </div> |
| )} |
| |
| <p className="note"> |
| توليد greedy بسيط بلا KV-cache — قد يستغرق ثوانٍ، والنتائج أولية بحكم قصر |
| التدريب. |
| </p> |
| </div> |
| </div> |
| ) |
| } |
|
|
| export default App |