| 173 | } |
| 174 | |
| 175 | export function stream( |
| 176 | chatPath: string, |
| 177 | requestPayload: any, |
| 178 | headers: any, |
| 179 | tools: any[], |
| 180 | funcs: Record<string, Function>, |
| 181 | controller: AbortController, |
| 182 | parseSSE: (text: string, runTools: any[]) => string | undefined, |
| 183 | processToolMessage: ( |
| 184 | requestPayload: any, |
| 185 | toolCallMessage: any, |
| 186 | toolCallResult: any[], |
| 187 | ) => void, |
| 188 | options: any, |
| 189 | ) { |
| 190 | let responseText = ""; |
| 191 | let remainText = ""; |
| 192 | let finished = false; |
| 193 | let running = false; |
| 194 | let runTools: any[] = []; |
| 195 | let responseRes: Response; |
| 196 | |
| 197 | // animate response to make it looks smooth |
| 198 | function animateResponseText() { |
| 199 | if (finished || controller.signal.aborted) { |
| 200 | responseText += remainText; |
| 201 | console.log("[Response Animation] finished"); |
| 202 | if (responseText?.length === 0) { |
| 203 | options.onError?.(new Error("empty response from server")); |
| 204 | } |
| 205 | return; |
| 206 | } |
| 207 | |
| 208 | if (remainText.length > 0) { |
| 209 | const fetchCount = Math.max(1, Math.round(remainText.length / 60)); |
| 210 | const fetchText = remainText.slice(0, fetchCount); |
| 211 | responseText += fetchText; |
| 212 | remainText = remainText.slice(fetchCount); |
| 213 | options.onUpdate?.(responseText, fetchText); |
| 214 | } |
| 215 | |
| 216 | requestAnimationFrame(animateResponseText); |
| 217 | } |
| 218 | |
| 219 | // start animaion |
| 220 | animateResponseText(); |
| 221 | |
| 222 | const finish = () => { |
| 223 | if (!finished) { |
| 224 | if (!running && runTools.length > 0) { |
| 225 | const toolCallMessage = { |
| 226 | role: "assistant", |
| 227 | tool_calls: [...runTools], |
| 228 | }; |
| 229 | running = true; |
| 230 | runTools.splice(0, runTools.length); // empty runTools |
| 231 | return Promise.all( |
| 232 | toolCallMessage.tool_calls.map((tool) => { |