spb/zyquo-cloud-web Public MIT
Zyquo Cloud Web — every cloud model, one beautiful chat, entirely in your browser.
TypeScript 81.9%
CSS 8.9%
JavaScript 7.5%
Shell 1.1%
HTML 0.6%
1/*2 * StreamTestView.tsx3 * Zyquo Cloud Web4 *5 * Author: Simon-Pierre Boucher6 * Mail: contact@spboucher.ai7 *8 * Phase 2 gate: a bare test view that renders a REAL streaming completion9 * token-by-token against any configured provider, using a key from10 * localStorage. Reached via #stream-test. Superseded by the real chat UI in11 * Phase 4 but kept for verification (Phase 7 drives it programmatically).12 */1314import { useRef, useState } from 'react'15import { cheapestModel, findModel, modelsFor } from '../providers/catalog'16import { clientFor } from '../providers/registry'17import { ProviderError } from '../providers/types'18import { getKey, configuredProviders } from '../storage/keys'19import { BUILT_IN_PROVIDERS, type Provider } from '../types'2021export default function StreamTestView() {22 const [provider, setProvider] = useState<Provider>('openai')23 const [modelID, setModelID] = useState('')24 const [prompt, setPrompt] = useState('Count from 1 to 10, one number per line.')25 const [output, setOutput] = useState('')26 const [reasoning, setReasoning] = useState('')27 const [status, setStatus] = useState('idle')28 const [tokens, setTokens] = useState('')29 const abortRef = useRef<AbortController | null>(null)3031 const run = async () => {32 const key = getKey(provider)33 if (!key) {34 setStatus(`no key for ${provider} in localStorage`)35 return36 }37 const model =38 (modelID !== '' ? findModel(provider, modelID) : undefined) ??39 cheapestModel(provider) ??40 modelsFor(provider)[0]41 if (!model) {42 setStatus('no model')43 return44 }45 setOutput('')46 setReasoning('')47 setTokens('')48 setStatus(`streaming ${model.id}…`)49 const controller = new AbortController()50 abortRef.current = controller51 try {52 const client = clientFor(model)53 const events = client.streamChat(54 {55 model,56 messages: [57 {58 id: 't1',59 role: 'user',60 text: prompt,61 createdAt: Date.now(),62 },63 ],64 parameters: { maxTokens: 256 },65 stream: true,66 },67 key,68 controller.signal69 )70 for await (const event of events) {71 if (event.type === 'textDelta') setOutput((prev) => prev + event.text)72 else if (event.type === 'reasoningDelta') setReasoning((prev) => prev + event.text)73 else if (event.type === 'usage')74 setTokens(`${event.usage.inputTokens}→${event.usage.outputTokens} tok`)75 else if (event.type === 'finished') setStatus(`finished (${event.reason ?? 'n/a'})`)76 }77 } catch (err) {78 setStatus(err instanceof ProviderError ? `${err.kind}: ${err.message}` : String(err))79 } finally {80 abortRef.current = null81 }82 }8384 return (85 <div86 style={{87 padding: 'var(--z-space-xl)',88 maxWidth: 720,89 margin: '0 auto',90 height: '100%',91 overflow: 'auto',92 fontFamily: 'var(--z-font-mono)',93 fontSize: 12.5,94 }}95 >96 <h2 style={{ fontFamily: 'var(--z-font-sans)' }}>Zyquo Cloud Web — stream test</h2>97 <p data-testid="configured">configured: {configuredProviders().join(', ') || '(none)'}</p>98 <div style={{ display: 'flex', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>99 <select100 data-testid="provider"101 value={provider}102 onChange={(e) => setProvider(e.target.value as Provider)}103 >104 {BUILT_IN_PROVIDERS.map((p) => (105 <option key={p} value={p}>106 {p}107 </option>108 ))}109 </select>110 <input111 data-testid="model"112 placeholder="model id (blank = cheapest)"113 value={modelID}114 onChange={(e) => setModelID(e.target.value)}115 style={{ width: 260 }}116 />117 <input118 data-testid="prompt"119 value={prompt}120 onChange={(e) => setPrompt(e.target.value)}121 style={{ width: 320 }}122 />123 <button data-testid="run" onClick={() => void run()}>124 Stream125 </button>126 <button data-testid="stop" onClick={() => abortRef.current?.abort()}>127 Stop128 </button>129 </div>130 <p data-testid="status">status: {status}</p>131 <p data-testid="tokens">{tokens}</p>132 {reasoning !== '' && (133 <pre134 data-testid="reasoning"135 style={{ whiteSpace: 'pre-wrap', color: 'var(--z-text-tertiary)' }}136 >137 {reasoning}138 </pre>139 )}140 <pre data-testid="output" style={{ whiteSpace: 'pre-wrap' }}>141 {output}142 </pre>143 </div>144 )145}146