summaryrefslogtreecommitdiff
path: root/internal/image
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-08 09:53:17 +0300
committerPaul Buetow <paul@buetow.org>2026-04-08 09:53:17 +0300
commitcd4265b8f87811db758c89bab5e62daa6c39337a (patch)
tree94c0a129b4b6ee9132baaa81eb3fbc96b083cbd4 /internal/image
parent7e7d34bfc0aec2aa8dff4cefa720e4a9b42d95d8 (diff)
feat(httpctx): add timeouts for OpenAI, Gemini, and HTTP downloads
Introduce internal/httpctx with non-zero http.Client timeouts for go-openai and google.golang.org/genai, shared image download client, and WithTimeoutUnlessSet for operation-level deadlines when callers use Background. Wire NewOpenAIClient/NewGenAIClient everywhere clients are constructed. Apply Search timeouts for DALL-E and Nano Banana, provider audio timeouts, model-list timeouts, Veo operation timeouts, story page download context, and single-word CLI processing cap. Made-with: Cursor
Diffstat (limited to 'internal/image')
-rw-r--r--internal/image/nanobanana.go7
-rw-r--r--internal/image/openai.go13
2 files changed, 14 insertions, 6 deletions
diff --git a/internal/image/nanobanana.go b/internal/image/nanobanana.go
index ac55f3b..34a83ba 100644
--- a/internal/image/nanobanana.go
+++ b/internal/image/nanobanana.go
@@ -16,6 +16,8 @@ import (
"time"
"google.golang.org/genai"
+
+ "codeberg.org/snonux/totalrecall/internal/httpctx"
)
const (
@@ -52,7 +54,7 @@ type NanoBananaClient struct {
// (ImageSearcher + AttributionProvider).
var _ ImageClient = (*NanoBananaClient)(nil)
-var newNanoBananaClient = genai.NewClient
+var newNanoBananaClient = httpctx.NewGenAIClient
var nanoBananaGenerateText = func(ctx context.Context, c *NanoBananaClient, model, systemPrompt, userPrompt string, temperature float32, maxOutputTokens int32) (string, error) {
return c.generateText(ctx, model, systemPrompt, userPrompt, temperature, maxOutputTokens)
}
@@ -84,6 +86,9 @@ func NewNanoBananaClient(config *NanoBananaConfig) *NanoBananaClient {
// Search generates an educational image for the Bulgarian word using Nano Banana.
func (c *NanoBananaClient) Search(ctx context.Context, opts *SearchOptions) ([]SearchResult, error) {
+ ctx, cancel := httpctx.WithTimeoutUnlessSet(ctx, httpctx.OperationTimeoutDefault)
+ defer cancel()
+
if err := c.ensureReady(); err != nil {
return nil, err
}
diff --git a/internal/image/openai.go b/internal/image/openai.go
index c277406..9fb8148 100644
--- a/internal/image/openai.go
+++ b/internal/image/openai.go
@@ -11,16 +11,16 @@ import (
"time"
"github.com/sashabaranov/go-openai"
+
+ "codeberg.org/snonux/totalrecall/internal/httpctx"
)
// Compile-time check that OpenAIClient implements the full ImageClient interface
// (ImageSearcher + AttributionProvider).
var _ ImageClient = (*OpenAIClient)(nil)
-// imageHTTPClient is a shared HTTP client with a generous timeout for image
-// downloads. http.DefaultClient has no timeout, which can block goroutines
-// indefinitely on slow or unresponsive servers.
-var imageHTTPClient = &http.Client{Timeout: 60 * time.Second}
+// imageHTTPClient is a shared HTTP client with a timeout for image downloads.
+var imageHTTPClient = httpctx.ImageDownloadHTTPClient()
// OpenAIClient implements ImageSearcher for OpenAI DALL-E image generation
type OpenAIClient struct {
@@ -52,7 +52,7 @@ func NewOpenAIClient(config *OpenAIConfig) *OpenAIClient {
return &OpenAIClient{}
}
- client := openai.NewClient(config.APIKey)
+ client := httpctx.NewOpenAIClient(config.APIKey)
// Set defaults
if config.Model == "" {
@@ -82,6 +82,9 @@ func NewOpenAIClient(config *OpenAIConfig) *OpenAIClient {
// Search generates an image for the Bulgarian word using DALL-E
func (c *OpenAIClient) Search(ctx context.Context, opts *SearchOptions) ([]SearchResult, error) {
+ ctx, cancel := httpctx.WithTimeoutUnlessSet(ctx, httpctx.OperationTimeoutDefault)
+ defer cancel()
+
if c.client == nil {
return nil, &SearchError{
Provider: "openai",