Skip to content

Commit 9b563cd

Browse files
metiu1claude
andcommitted
v0.3.58: don't kill long cloud generations at 120s (streaming timeout fix)
Cloud calls used http.Client{Timeout: 120s}, which caps the ENTIRE streamed response. Long generations (e.g. writing a whole story into a document) exceeded 120s and were cut off — surfacing as "Can't reach the assistant" after 120s. Switch streaming cloud calls to a client that caps connect/TLS/time-to-first- byte but not the total stream duration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 90b73e0 commit 9b563cd

6 files changed

Lines changed: 26 additions & 11 deletions

File tree

vortelio-pip/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "vortelio"
7-
version = "0.3.57"
7+
version = "0.3.58"
88
description = "Local-first AI platform. Run LLMs, generate images & video, transcribe audio, create 3D — on your own machine. OpenAI & Ollama API compatible. Apache 2.0."
99
readme = "README.md"
1010
requires-python = ">=3.8"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.3.57"
1+
__version__ = "0.3.58"

vortelio-pip/src/vortelio_cli/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import urllib.request
1111
from pathlib import Path
1212

13-
VERSION = "0.3.57"
13+
VERSION = "0.3.58"
1414
RELEASE_BASE = os.environ.get(
1515
"VORTELIO_RELEASE_BASE",
1616
f"https://github.com/metiu1/Vortelio/releases/download/v{VERSION}",
512 Bytes
Binary file not shown.

vortelio/internal/cloud/cloud.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"encoding/json"
88
"fmt"
99
"io"
10+
"net"
1011
"net/http"
1112
"os"
1213
"path/filepath"
@@ -16,6 +17,20 @@ import (
1617
"github.com/vortelio/vortelio/internal/config"
1718
)
1819

20+
// streamingHTTPClient is used for streaming cloud calls. It caps connection, TLS
21+
// and time-to-first-byte, but does NOT cap the total streaming duration — so long
22+
// generations (e.g. writing a whole document/story) are not killed mid-stream the
23+
// way a flat 120s client.Timeout did ("Can't reach the assistant" after 120s).
24+
var streamingHTTPClient = &http.Client{
25+
Transport: &http.Transport{
26+
Proxy: http.ProxyFromEnvironment,
27+
DialContext: (&net.Dialer{Timeout: 30 * time.Second}).DialContext,
28+
TLSHandshakeTimeout: 30 * time.Second,
29+
ResponseHeaderTimeout: 120 * time.Second,
30+
IdleConnTimeout: 90 * time.Second,
31+
},
32+
}
33+
1934
// truncToolResult caps a tool result fed back to a cloud model so big outputs
2035
// (e.g. list_directory of a large repo) don't bloat the request and trigger
2136
// provider 500s. The UI still receives the full result via the tool event.
@@ -326,7 +341,7 @@ func chatOpenAI(p Provider, apiKey string, messages []Message, onToken func(stri
326341
req.Header.Set("X-Title", "Vortelio")
327342
}
328343

329-
client := &http.Client{Timeout: 120 * time.Second}
344+
client := streamingHTTPClient
330345
resp, err := client.Do(req)
331346
if err != nil {
332347
return "", fmt.Errorf("network error: %w", err)
@@ -405,7 +420,7 @@ func chatAnthropic(p Provider, apiKey string, messages []Message, onToken func(s
405420
req.Header.Set("x-api-key", apiKey)
406421
req.Header.Set("anthropic-version", "2023-06-01")
407422

408-
client := &http.Client{Timeout: 120 * time.Second}
423+
client := streamingHTTPClient
409424
resp, err := client.Do(req)
410425
if err != nil {
411426
return "", fmt.Errorf("network error: %w", err)
@@ -486,7 +501,7 @@ func chatOpenAIWithTools(p Provider, apiKey string, messages []Message, opts *To
486501
req.Header.Set("X-Title", "Vortelio")
487502
}
488503

489-
client := &http.Client{Timeout: 120 * time.Second}
504+
client := streamingHTTPClient
490505
resp, err := client.Do(req)
491506
if err != nil {
492507
return "", fmt.Errorf("network error: %w", err)
@@ -658,7 +673,7 @@ func chatOpenAIWithTools(p Provider, apiKey string, messages []Message, opts *To
658673
req.Header.Set("HTTP-Referer", "https://vortelio.app")
659674
req.Header.Set("X-Title", "Vortelio")
660675
}
661-
client := &http.Client{Timeout: 120 * time.Second}
676+
client := streamingHTTPClient
662677
if resp, err := client.Do(req); err == nil {
663678
if resp.StatusCode == 200 {
664679
scanner := bufio.NewScanner(resp.Body)
@@ -782,7 +797,7 @@ func chatAnthropicWithTools(p Provider, apiKey string, messages []Message, opts
782797
req.Header.Set("x-api-key", apiKey)
783798
req.Header.Set("anthropic-version", "2023-06-01")
784799

785-
client := &http.Client{Timeout: 120 * time.Second}
800+
client := streamingHTTPClient
786801
resp, err := client.Do(req)
787802
if err != nil {
788803
return "", fmt.Errorf("network error: %w", err)
@@ -990,7 +1005,7 @@ func chatGeminiWithTools(p Provider, apiKey string, messages []Message, opts *To
9901005
maxRounds = opts.MaxRounds
9911006
}
9921007
url := p.BaseURL + "?key=" + apiKey
993-
client := &http.Client{Timeout: 120 * time.Second}
1008+
client := streamingHTTPClient
9941009

9951010
for round := 0; round < maxRounds; round++ {
9961011
_ = round
@@ -1175,7 +1190,7 @@ func chatGemini(p Provider, apiKey string, messages []Message, onToken func(stri
11751190
}
11761191
req.Header.Set("Content-Type", "application/json")
11771192

1178-
client := &http.Client{Timeout: 120 * time.Second}
1193+
client := streamingHTTPClient
11791194
resp, err := client.Do(req)
11801195
if err != nil {
11811196
return "", fmt.Errorf("network error: %w", err)

vortelio/internal/version/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ package version
33
// Version è impostabile a build time via:
44
//
55
// -ldflags "-X github.com/vortelio/vortelio/internal/version.Version=X.Y.Z"
6-
var Version = "0.3.57"
6+
var Version = "0.3.58"

0 commit comments

Comments
 (0)