diff --git a/index.html b/index.html index 18cfdfc..1c0c6de 100644 --- a/index.html +++ b/index.html @@ -215,84 +215,53 @@ const WS_URL = "wss://orp.awww.workers.dev/ws"; class SingleRunWS { constructor({ payload, onDelta }) { - this.payload = payload; - this.onDelta = onDelta; - this.ws = null; - this.lastSeq = -1; - this.done = false; - this._vis = this._onVisibility.bind(this); + this.payload=payload; this.onDelta=onDelta; + this.ws=null; this.lastSeq=-1; + this.done=false; this.manual=false; + this.timer=null; this.backoff=300; } - start() { - window.SUNE.log("WS start"); - this._connect("begin"); - document.addEventListener("visibilitychange", this._vis, { passive: true }); + start(){ this.#connect("begin"); } + abort(){ + this.manual=true; this.done=true; + try{ this.ws?.send(JSON.stringify({type:"stop"})) }catch{} + try{ this.ws?.close() }catch{} + clearTimeout(this.timer); } - abort() { - window.SUNE.log("WS abort"); - this.done = true; - try { this.ws?.close(); } catch {} - document.removeEventListener("visibilitychange", this._vis); - } - _onVisibility() { - window.SUNE.log(`WS visibility: ${document.visibilityState}`); - if (this.done) return; - if (document.visibilityState === "hidden") { - try { this.ws?.close(); } catch {} - } else if (!this.ws || this.ws.readyState > 1) { - this._connect("resume"); - } - } - _connect(mode) { - if (this.done) return; - window.SUNE.log(`WS connect [${mode}]`); - this.ws = new WebSocket(WS_URL); - this.ws.onopen = () => { - window.SUNE.log("WS open"); - if (mode === "begin") { - this.ws.send(JSON.stringify({ type: "begin", ...this.payload, after: this.lastSeq })); - } else { - this.ws.send(JSON.stringify({ type: "resume", after: this.lastSeq })); - } + #connect(mode){ + if(this.done)return; + this.ws=new WebSocket(WS_URL); + this.ws.onopen=()=>{ + if(mode==="begin") this.ws.send(JSON.stringify({type:"begin",...this.payload,after:this.lastSeq})); + else this.ws.send(JSON.stringify({type:"resume",after:this.lastSeq})); }; - this.ws.onmessage = (e) => { - // window.SUNE.log(`WS msg: ${e.data.slice(0,80)}`); - let m; try { m = JSON.parse(e.data); } catch { return; } - if (m.type === "delta" && typeof m.seq === "number" && m.seq > this.lastSeq) { - this.lastSeq = m.seq; - this.onDelta(m.text, false); - } else if (m.type === "done") { - this.done = true; - this.onDelta("", true); - this.abort(); - } else if (m.type === "err") { - this.done = true; - this.onDelta(`\n\n${m.message || "error"}`, true); - this.abort(); - } + this.ws.onmessage=e=>{ + let m; try{m=JSON.parse(e.data)}catch{return}; + if(m.type==="delta"&&m.seq>this.lastSeq){this.lastSeq=m.seq;this.onDelta(m.text,false);} + else if(m.type==="done"){this.done=true;this.onDelta("",true);this.#cleanup();} + else if(m.type==="err"){this.done=true;this.onDelta("\\n\\n"+(m.message||"error"),true);this.#cleanup();} }; - this.ws.onerror = (e) => window.SUNE.log("WS error" + (e?.message?": "+e.message:"")); - this.ws.onclose = () => window.SUNE.log("WS close"); + this.ws.onclose=()=>{ if(!this.done&&!this.manual) this.#schedule(); }; } + #schedule(){ + clearTimeout(this.timer); + if(this.manual||this.done)return; + this.timer=setTimeout(()=>{this.#connect("resume"); this.backoff=Math.min(this.backoff*1.5,5000)},this.backoff); + } + #cleanup(){ clearTimeout(this.timer); try{this.ws?.close()}catch{} } } -async function askViaDOStreaming(onDelta) { - const apiKey = store.apiKey; - const model = store.model; - const msgs = []; - if (store.system_prompt) msgs.push({ role: "system", content: [{ type: "text", text: store.system_prompt }] }); - msgs.push(...state.messages.filter(m => m.role !== "system").map(m => ({ role: m.role, content: m.content }))); +async function askViaDOStreaming(onDelta){ + const apiKey=store.apiKey, model=store.model; + const msgs=[]; if(store.system_prompt) msgs.push({role:"system",content:[{type:"text",text:store.system_prompt}]}); + msgs.push(...state.messages.filter(m=>m.role!=="system").map(m=>({role:m.role,content:m.content}))); - const runner = new SingleRunWS({ - payload: { apiKey, model, messages: msgs }, - onDelta - }); - - state.controller = { abort: () => runner.abort() }; + const runner=new SingleRunWS({payload:{apiKey,model,messages:msgs},onDelta}); + state.controller={abort:()=>runner.abort()}; runner.start(); - return { ok: true }; + return {ok:true}; } -askOpenRouterStreaming = askViaDOStreaming; +askOpenRouterStreaming=askViaDOStreaming;