diff --git a/backend/internal/provider/imagine/image.go b/backend/internal/provider/imagine/image.go index 125d616..07a126b 100644 --- a/backend/internal/provider/imagine/image.go +++ b/backend/internal/provider/imagine/image.go @@ -97,7 +97,12 @@ func (c *Client) GenerateImage(ctx context.Context, cred string, styleID int, re func (c *Client) pollImage(ctx context.Context, token, userID, batchID string) (string, error) { ticker := time.NewTicker(3 * time.Second) defer ticker.Stop() + // Poll for the full generation budget (caller's genCtx), leaving headroom for + // the download, instead of a shorter hardcoded cap that killed slow jobs early. deadline := time.Now().Add(4 * time.Minute) + if dl, ok := ctx.Deadline(); ok { + deadline = dl.Add(-60 * time.Second) + } url := teamsBase + "/v1/org/" + userID + "/objects?batch=true&limit=50&service=image,chat-image" for { diff --git a/backend/internal/provider/krea/image.go b/backend/internal/provider/krea/image.go index d877e90..86199ed 100644 --- a/backend/internal/provider/krea/image.go +++ b/backend/internal/provider/krea/image.go @@ -181,7 +181,12 @@ func (c *Client) GenerateImage(ctx context.Context, cookie, prompt string, width func (c *Client) pollImage(ctx context.Context, cookie, jobID string) (string, error) { ticker := time.NewTicker(3 * time.Second) defer ticker.Stop() + // Poll for the full generation budget (caller's genCtx), leaving headroom for + // the download, instead of a shorter hardcoded cap that killed slow jobs early. deadline := time.Now().Add(4 * time.Minute) + if dl, ok := ctx.Deadline(); ok { + deadline = dl.Add(-60 * time.Second) + } for { body, status, err := c.apiGetP(ctx, cookie, "/api/job-status?id="+jobID, false) diff --git a/backend/internal/provider/leonardo/image.go b/backend/internal/provider/leonardo/image.go index 33b8e91..fb0d26d 100644 --- a/backend/internal/provider/leonardo/image.go +++ b/backend/internal/provider/leonardo/image.go @@ -252,8 +252,13 @@ func (c *Client) pollImage(ctx context.Context, accessToken, genID string) (stri ticker := time.NewTicker(3 * time.Second) defer ticker.Stop() - // Cap the wait independent of the parent deadline so a stuck job can't hang. + // Poll for the full generation budget (caller's genCtx), leaving headroom for + // the download, instead of a shorter hardcoded cap that killed slow jobs early. + // ctx already bounds the wait, so a stuck job still can't hang indefinitely. deadline := time.Now().Add(5 * time.Minute) + if dl, ok := ctx.Deadline(); ok { + deadline = dl.Add(-60 * time.Second) + } for { body, status, err := c.graphqlP(ctx, accessToken, payload, false)