Add server-side timeout handling to Ollama Cloud streaming
- Added 60 second timeout per chunk in parseStreamingResponse - Added 120 second timeout to makeRequest with AbortController - This prevents the server from hanging indefinitely on slow/unresponsive API This should fix the UI freeze when sending messages to Ollama Cloud models.
This commit is contained in:
@@ -338,12 +338,39 @@ export class OllamaCloudClient {
|
|||||||
|
|
||||||
const reader = response.body.getReader()
|
const reader = response.body.getReader()
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder()
|
||||||
|
const STREAM_TIMEOUT_MS = 60000 // 60 second timeout per chunk
|
||||||
|
let lastActivity = Date.now()
|
||||||
|
|
||||||
|
const checkTimeout = () => {
|
||||||
|
if (Date.now() - lastActivity > STREAM_TIMEOUT_MS) {
|
||||||
|
reader.cancel().catch(() => { })
|
||||||
|
throw new Error("Stream timeout - no data received for 60 seconds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read()
|
checkTimeout()
|
||||||
|
|
||||||
|
// Create a timeout promise
|
||||||
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||||
|
setTimeout(() => reject(new Error("Read timeout")), STREAM_TIMEOUT_MS)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Race the read against the timeout
|
||||||
|
let result: ReadableStreamReadResult<Uint8Array>
|
||||||
|
try {
|
||||||
|
result = await Promise.race([reader.read(), timeoutPromise])
|
||||||
|
} catch (timeoutError) {
|
||||||
|
reader.cancel().catch(() => { })
|
||||||
|
throw new Error("Stream read timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { done, value } = result
|
||||||
if (done) break
|
if (done) break
|
||||||
|
|
||||||
|
lastActivity = Date.now()
|
||||||
|
|
||||||
const lines = decoder.decode(value, { stream: true }).split('\n').filter(line => line.trim())
|
const lines = decoder.decode(value, { stream: true }).split('\n').filter(line => line.trim())
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
@@ -371,7 +398,7 @@ export class OllamaCloudClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async makeRequest(endpoint: string, options: RequestInit): Promise<Response> {
|
private async makeRequest(endpoint: string, options: RequestInit, timeoutMs: number = 120000): Promise<Response> {
|
||||||
// Ensure endpoint starts with /api
|
// Ensure endpoint starts with /api
|
||||||
const apiEndpoint = endpoint.startsWith('/api') ? endpoint : `/api${endpoint}`
|
const apiEndpoint = endpoint.startsWith('/api') ? endpoint : `/api${endpoint}`
|
||||||
const url = `${this.baseUrl}${apiEndpoint}`
|
const url = `${this.baseUrl}${apiEndpoint}`
|
||||||
@@ -386,10 +413,19 @@ export class OllamaCloudClient {
|
|||||||
|
|
||||||
console.log(`[OllamaCloud] Making request to: ${url}`)
|
console.log(`[OllamaCloud] Making request to: ${url}`)
|
||||||
|
|
||||||
return fetch(url, {
|
// Add timeout to prevent indefinite hangs
|
||||||
...options,
|
const controller = new AbortController()
|
||||||
headers
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
|
||||||
})
|
|
||||||
|
try {
|
||||||
|
return await fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers,
|
||||||
|
signal: controller.signal
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCloudModels(): Promise<OllamaModel[]> {
|
async getCloudModels(): Promise<OllamaModel[]> {
|
||||||
|
|||||||
Reference in New Issue
Block a user