Deployment.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { useState } from 'react'
  2. import api, { DeployResponse } from '../api/client'
  3. export function Deployment() {
  4. const [jobId, setJobId] = useState('')
  5. const [mergeWithBase, setMergeWithBase] = useState(false)
  6. const [exportFormat, setExportFormat] = useState('safetensors')
  7. const [running, setRunning] = useState(false)
  8. const [result, setResult] = useState<DeployResponse | null>(null)
  9. const handleExport = () => {
  10. if (!jobId.trim()) return
  11. setRunning(true)
  12. api.deployment.export({
  13. job_id: jobId,
  14. merge_with_base: mergeWithBase,
  15. export_format: exportFormat,
  16. })
  17. .then(setResult)
  18. .catch(console.error)
  19. .finally(() => setRunning(false))
  20. }
  21. return (
  22. <div>
  23. <h1>部署</h1>
  24. <div style={{ marginTop: 16, background: '#fff', borderRadius: 8, padding: 20, boxShadow: '0 1px 3px rgba(0,0,0,0.1)' }}>
  25. <h2 style={{ margin: '0 0 16px', fontSize: 16 }}>导出 Adapter</h2>
  26. <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12, alignItems: 'end' }}>
  27. <div>
  28. <label style={{ display: 'block', fontSize: 12, color: '#666', marginBottom: 4 }}>训练任务 ID</label>
  29. <input value={jobId} onChange={e => setJobId(e.target.value)} placeholder="任务 ID" style={{ width: '100%', padding: '6px 8px', borderRadius: 4, border: '1px solid #ccc', boxSizing: 'border-box' }} />
  30. </div>
  31. <div>
  32. <label style={{ display: 'block', fontSize: 12, color: '#666', marginBottom: 4 }}>导出格式</label>
  33. <select value={exportFormat} onChange={e => setExportFormat(e.target.value)} style={{ width: '100%', padding: '6px 8px', borderRadius: 4, border: '1px solid #ccc', boxSizing: 'border-box' }}>
  34. <option value="safetensors">SafeTensors</option>
  35. <option value="gguf">GGUF</option>
  36. </select>
  37. </div>
  38. <div>
  39. <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, cursor: 'pointer' }}>
  40. <input type="checkbox" checked={mergeWithBase} onChange={e => setMergeWithBase(e.target.checked)} />
  41. 合并基础模型
  42. </label>
  43. </div>
  44. </div>
  45. <button
  46. onClick={handleExport}
  47. disabled={running}
  48. style={{ marginTop: 16, padding: '8px 24px', borderRadius: 4, border: 'none', background: '#e94560', color: '#fff', cursor: 'pointer', opacity: running ? 0.6 : 1 }}
  49. >
  50. {running ? '导出中...' : '开始导出'}
  51. </button>
  52. </div>
  53. {result && (
  54. <div style={{ marginTop: 24, background: '#fff', borderRadius: 8, padding: 20, boxShadow: '0 1px 3px rgba(0,0,0,0.1)' }}>
  55. <h3>导出状态</h3>
  56. <p><strong>任务 ID:</strong> {result.job_id}</p>
  57. <p><strong>状态:</strong> <span style={{ color: result.error ? '#e94560' : '#4caf50' }}>{result.status}</span></p>
  58. {result.output_path && <p><strong>输出路径:</strong> {result.output_path}</p>}
  59. {result.error && <p style={{ color: '#e94560' }}><strong>错误:</strong> {result.error}</p>}
  60. </div>
  61. )}
  62. </div>
  63. )
  64. }