Skip to content

feat: 2-second wall-clock timeout for Cloud Rewrite#16

Merged
ZhaoChaoqun merged 1 commit into
mainfrom
feat/cloud-rewrite-timeout
Mar 23, 2026
Merged

feat: 2-second wall-clock timeout for Cloud Rewrite#16
ZhaoChaoqun merged 1 commit into
mainfrom
feat/cloud-rewrite-timeout

Conversation

@ZhaoChaoqun

Copy link
Copy Markdown
Owner

Summary

  • Cloud Rewrite API 调用有时超过 12 秒(日志实测 11.6s),严重影响用户体验
  • 使用 withThrowingTaskGroup 实现 API 调用 vs 2 秒 deadline 竞赛
  • 超时后立即返回原始转录文本,取消在途 HTTP 请求释放资源
  • URLRequest.timeoutInterval 从 15s 降至 3s 作为安全网

Test plan

  • 验证正常响应(<2s)时返回 rewrite 结果
  • 验证慢响应(>2s)时返回原始文本,日志显示 timeout warning
  • 验证网络错误仍正确 fallback
  • Release build 验证

🤖 Generated with Claude Code

Use withThrowingTaskGroup to race the API call against a 2s deadline.
If the Cerebras API doesn't respond within 2 seconds, the original
transcription text is used immediately — no more 12-second waits.

Implementation:
- TaskGroup race: API call vs Task.sleep(2s), first result wins
- Loser is cancelled via group.cancelAll() (frees URLSession resources)
- URLRequest.timeoutInterval lowered from 15s to 3s as safety net
- Timeout events logged for monitoring
- rewriteTimeout defined as named Duration constant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ZhaoChaoqun ZhaoChaoqun merged commit 77c70ad into main Mar 23, 2026
5 checks passed

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Clean implementation of a 2-second wall-clock timeout for the Cloud Rewrite API using withThrowingTaskGroup racing pattern.

What looks good:

  • The race pattern correctly uses structured concurrency — if the timeout fires, RewriteTimeoutError propagates and the API task is cancelled via structured cancellation.
  • URLSession.data(for:) respects cooperative cancellation, so the in-flight HTTP request is properly cleaned up.
  • The URLRequest.timeoutInterval = 3 safety net is a sensible backstop.
  • Model probe is launched eagerly at init time, so it rarely eats into the 2s budget.

No blocking issues found. One minor observation posted inline.

return try await withThrowingTaskGroup(of: String.self) { group in
// Race 1: the actual API call
group.addTask { [self] in
try await self.rewrite(text: text, apiKey: apiKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: if modelProbeTask hasn't resolved yet when this runs (line 159), the await modelProbeTask.value eats into the 2s budget. Since the probe is fired eagerly at init, this is unlikely in steady state — but on a cold start with a slow /v1/models endpoint, the first rewrite call could time out before the actual chat completion request even begins.

Not blocking — just worth being aware of. If it becomes an issue in practice, you could move the model resolution outside the racing task group so the 2s clock only starts when the HTTP call is made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant