-
Notifications
You must be signed in to change notification settings - Fork 0
[codex] Expose sync timeout as queryable error #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,12 +135,15 @@ type ClientOptions struct { | |
| } | ||
|
|
||
| type prediction struct { | ||
| ID string `json:"id"` | ||
| Model string `json:"model"` | ||
| Status string `json:"status"` | ||
| Input any `json:"input"` | ||
| Outputs []any `json:"outputs"` | ||
| Error string `json:"error"` | ||
| ID string `json:"id"` | ||
| Model string `json:"model"` | ||
| Status string `json:"status"` | ||
| Input any `json:"input"` | ||
| Outputs []any `json:"outputs"` | ||
| Error string `json:"error"` | ||
| Code int `json:"code"` | ||
| CreatedAt string `json:"created_at"` | ||
| URLs map[string]string `json:"urls"` | ||
| } | ||
|
|
||
| type predictionResponse struct { | ||
|
|
@@ -288,10 +291,13 @@ func (c *Client) submit(model string, input map[string]any, enableSyncMode bool, | |
| if enableSyncMode { | ||
| return "", map[string]any{ | ||
| "data": map[string]any{ | ||
| "id": result.Data.ID, | ||
| "status": result.Data.Status, | ||
| "error": result.Data.Error, | ||
| "outputs": result.Data.Outputs, | ||
| "id": result.Data.ID, | ||
| "status": result.Data.Status, | ||
| "error": result.Data.Error, | ||
| "outputs": result.Data.Outputs, | ||
| "code": result.Data.Code, | ||
| "created_at": result.Data.CreatedAt, | ||
| "urls": result.Data.URLs, | ||
| }, | ||
| }, nil | ||
| } | ||
|
|
@@ -423,12 +429,68 @@ func (c *Client) isRetryableError(err error) bool { | |
| } | ||
|
|
||
| errStr := strings.ToLower(err.Error()) | ||
| if strings.Contains(errStr, "sync mode timed out") { | ||
| return false | ||
| } | ||
| return strings.Contains(errStr, "timeout") || | ||
| strings.Contains(errStr, "connection") || | ||
| strings.Contains(errStr, "http 5") || | ||
| strings.Contains(errStr, "429") | ||
| } | ||
|
|
||
| func syncResultURL(data map[string]any) string { | ||
| switch urls := data["urls"].(type) { | ||
| case map[string]string: | ||
| return urls["get"] | ||
| case map[string]any: | ||
| if resultURL, ok := urls["get"].(string); ok { | ||
| return resultURL | ||
| } | ||
| } | ||
| return "" | ||
| } | ||
|
|
||
| func syncResultCode(data map[string]any) int { | ||
| switch code := data["code"].(type) { | ||
| case int: | ||
| return code | ||
| case int64: | ||
| return int(code) | ||
| case float64: | ||
| return int(code) | ||
| } | ||
| return 0 | ||
| } | ||
|
|
||
| func isSyncTimeoutData(data map[string]any) bool { | ||
| errorMsg, _ := data["error"].(string) | ||
| status, _ := data["status"].(string) | ||
| return syncResultCode(data) == 5004 || | ||
| (status == "processing" && strings.Contains(errorMsg, "Sync mode timed out")) | ||
|
Comment on lines
+468
to
+469
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a sync response is already terminal with Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| func syncModeError(data map[string]any) error { | ||
| errorMsg := "Unknown error" | ||
| if e, ok := data["error"].(string); ok && e != "" { | ||
| errorMsg = e | ||
| } | ||
|
|
||
| requestID := "unknown" | ||
| if id, ok := data["id"].(string); ok && id != "" { | ||
| requestID = id | ||
| } | ||
|
|
||
| if isSyncTimeoutData(data) { | ||
| message := fmt.Sprintf("sync mode timed out (task_id: %s): %s", requestID, errorMsg) | ||
| if resultURL := syncResultURL(data); resultURL != "" && !strings.Contains(message, resultURL) { | ||
| message += " Query the result later at: " + resultURL | ||
| } | ||
| return errors.New(message) | ||
| } | ||
|
|
||
| return fmt.Errorf("prediction failed (task_id: %s): %s", requestID, errorMsg) | ||
| } | ||
|
|
||
| // Run executes a model and waits for the output. | ||
| func (c *Client) Run(model string, input map[string]any, opts ...RunOption) (map[string]any, error) { | ||
| // Apply default options | ||
|
|
@@ -463,15 +525,7 @@ func (c *Client) Run(model string, input map[string]any, opts ...RunOption) (map | |
|
|
||
| status, _ := data["status"].(string) | ||
| if status != "completed" { | ||
| errorMsg := "Unknown error" | ||
| if e, ok := data["error"].(string); ok && e != "" { | ||
| errorMsg = e | ||
| } | ||
| requestIDStr := "unknown" | ||
| if id, ok := data["id"].(string); ok && id != "" { | ||
| requestIDStr = id | ||
| } | ||
| return nil, fmt.Errorf("prediction failed (task_id: %s): %s", requestIDStr, errorMsg) | ||
| return nil, syncModeError(data) | ||
| } | ||
|
|
||
| outputs, ok := data["outputs"] | ||
|
|
@@ -510,6 +564,7 @@ type RunDetail struct { | |
| Model string `json:"model"` | ||
| Error string `json:"error,omitempty"` | ||
| CreatedAt string `json:"createdAt,omitempty"` | ||
| ResultURL string `json:"resultUrl,omitempty"` | ||
| } | ||
|
|
||
| // RunNoThrowResult is the result of RunNoThrow method. | ||
|
|
@@ -584,14 +639,21 @@ func (c *Client) RunNoThrow(model string, input map[string]any, opts ...RunOptio | |
| errorMsg = e | ||
| } | ||
| createdAt, _ := data["created_at"].(string) | ||
| resultURL := syncResultURL(data) | ||
| detailStatus := "failed" | ||
| if isSyncTimeoutData(data) { | ||
| detailStatus = "processing" | ||
| errorMsg = syncModeError(data).Error() | ||
| } | ||
| return &RunNoThrowResult{ | ||
| Outputs: nil, | ||
| Detail: RunDetail{ | ||
| TaskID: taskID, | ||
| Status: "failed", | ||
| Status: detailStatus, | ||
| Model: model, | ||
| Error: errorMsg, | ||
| CreatedAt: createdAt, | ||
| ResultURL: resultURL, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With default client settings, this documented path only works if the server returns before 10 seconds:
submitstill setshttp.Client.Timeoutfromc.connectionTimeout(10s default), so a server-side sync wait that times out after the API's longer wait window is cut off locally before the JSON body containing the task ID/URLs can be decoded. In that sync-timeout case callers get a generic client timeout (and task retries may resubmit) instead of the queryable task error promised here; the sync POST needs a read/request timeout based on the run timeout, or this doc must require increasing the connection timeout.Useful? React with 👍 / 👎.