Spaces:
Sleeping
Sleeping
File size: 4,119 Bytes
9aee46b |
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 |
import React, { useState } from 'react';
import {
Paper,
Typography,
Grid,
TextField,
Button,
Divider
} from '@material-ui/core';
function OpenAIChat() {
const [model, setModel] = useState('gpt-4o-mini');
const [apiKey, setApiKey] = useState('');
const [system, setSystem] = useState('You are a helpful assistant.');
const [prompt, setPrompt] = useState('');
const [response, setResponse] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const onSend = async () => {
setError('');
setResponse('');
const p = (prompt || '').trim();
if (!p) { setError('Please enter a question.'); return; }
setLoading(true);
try {
const res = await fetch('/api/openai/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
prompt: p,
model: (model || '').trim() || 'gpt-4o-mini',
api_key: (apiKey || undefined),
system: (system || undefined)
})
});
if (!res.ok) {
let txt = await res.text();
try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}
throw new Error(txt);
}
const data = await res.json();
const meta = `Model: ${data.model} | Latency: ${data.latency_sec}s` + (data.usage ? ` | Usage: ${JSON.stringify(data.usage)}` : '');
setResponse((data.response || '(Empty response)') + '\n\n---\n' + meta);
} catch (e) {
setError('Error: ' + e.message);
} finally {
setLoading(false);
}
};
const onClear = () => {
setPrompt('');
setResponse('');
setError('');
};
return (
<Paper style={{ padding: 16 }}>
<Typography variant="h5" gutterBottom>
OpenAI Chat (OpenAI API)
</Typography>
<Typography variant="body2" color="textSecondary" gutterBottom>
If the server env var OPENAI_API_KEY is set, the API Key field is optional.
</Typography>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<TextField
label="Model"
value={model}
onChange={(e) => setModel(e.target.value)}
fullWidth
variant="outlined"
size="small"
/>
</Grid>
<Grid item xs={12} md={6}>
<TextField
label="OpenAI API Key (optional)"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
fullWidth
variant="outlined"
size="small"
type="password"
placeholder="sk-..."
/>
</Grid>
<Grid item xs={12}>
<TextField
label="System Prompt (optional)"
value={system}
onChange={(e) => setSystem(e.target.value)}
fullWidth
variant="outlined"
size="small"
/>
</Grid>
<Grid item xs={12}>
<TextField
label="User Question"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
fullWidth
multiline
rows={4}
variant="outlined"
/>
</Grid>
{error && (
<Grid item xs={12}>
<Typography color="error">{error}</Typography>
</Grid>
)}
<Grid item xs={12}>
<div style={{ display: 'flex', gap: 8 }}>
<Button color="primary" variant="contained" onClick={onSend} disabled={loading}>
{loading ? 'Sending...' : 'Send Question'}
</Button>
<Button variant="outlined" onClick={onClear}>Clear</Button>
</div>
</Grid>
<Grid item xs={12}>
<Divider style={{ margin: '12px 0' }} />
<Typography variant="subtitle2" color="textSecondary">Response</Typography>
<pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>
</Grid>
</Grid>
</Paper>
);
}
export default OpenAIChat;
|