Prewarm API connections while recording + latency instrumentation#264
Prewarm API connections while recording + latency instrumentation#264swimmer-303 wants to merge 4 commits into
Conversation
The session-level timeoutIntervalForResource=30 silently overrode the transcription_timeout_seconds, post_processing_timeout_seconds, and context_request_timeout_seconds settings, killing long transfers to slow local models. Sessions are now cached per resource timeout derived from each request's configured timeout, preserving connection reuse. Uploads keep their fresh-session-per-call behavior. Fixes zachlatta#253 (cherry picked from commit 9e89b6e)
Transcription uploads deliberately use a fresh URLSession per upload, which put a full DNS + TCP + TLS handshake on the critical stop-to-paste path of every dictation. Now a one-shot session is created and its connection opened when recording starts, and the next upload to the same host consumes it — still one session per upload, so the connection-poisoning defense is unchanged. The shared data session is warmed the same way for post-processing and context requests. (cherry picked from commit 08506c9)
(cherry picked from commit 848b67c)
📝 WalkthroughWalkthroughAdds timeout-aware URL session reuse, API connection prewarming during recording startup, configurable service timeouts, request-duration logging, and release notes describing latency and timeout fixes. ChangesTransport and recording pipeline
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant AppState
participant LLMAPITransport
participant TranscriptionService
participant PostProcessingService
participant URLSession
AppState->>LLMAPITransport: prewarm transcription upload connection
AppState->>LLMAPITransport: prewarm post-processing shared connection
LLMAPITransport->>URLSession: send asynchronous HEAD handshakes
TranscriptionService->>LLMAPITransport: upload audio with configured timeout
PostProcessingService->>LLMAPITransport: send processing request with configured timeout
LLMAPITransport->>URLSession: reuse timeout-matched sessions
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
Sources/LLMAPITransport.swift (1)
27-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMinor duplication of the resource-timeout clamp.
resourceTimeout(for:)andprewarmUploadConnection'slet timeout = max(expectedRequestTimeout, minimumResourceTimeout)compute the identical clamp from two different inputs. Consider a single helper, e.g.clampedResourceTimeout(_ interval: TimeInterval) -> TimeInterval, called from bothresourceTimeout(for:)andprewarmUploadConnection, to keep the floor logic in one place.Also applies to: 54-56
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/LLMAPITransport.swift` around lines 27 - 29, Extract the shared minimum-timeout clamp into a helper such as clampedResourceTimeout(_:), then update resourceTimeout(for:) and prewarmUploadConnection to call it instead of using max(..., minimumResourceTimeout) directly.Sources/PostProcessingService.swift (1)
469-477: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDuplicate timing/logging block between
processandprocessCommandTransform.The
t0/os_logcompletion-timing block is identical in both methods. Since this file already duplicates most of the request-building logic between these two methods, consider extracting a small helper (e.g.LLMAPITransport.data(for:)wrapped in atimedLLMRequest(_:)on this service) that performs the request and logs elapsed time/model in one place, to avoid a third copy if another call site is added later.Also applies to: 608-616
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/PostProcessingService.swift` around lines 469 - 477, Extract the duplicated timed LLM request logic from process and processCommandTransform into a shared helper such as timedLLMRequest(_:), using LLMAPITransport.data(for:) internally and logging elapsed time with the model. Replace both existing t0/os_log blocks with calls to this helper, preserving their returned data and response behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@Sources/LLMAPITransport.swift`:
- Around line 27-29: Extract the shared minimum-timeout clamp into a helper such
as clampedResourceTimeout(_:), then update resourceTimeout(for:) and
prewarmUploadConnection to call it instead of using max(...,
minimumResourceTimeout) directly.
In `@Sources/PostProcessingService.swift`:
- Around line 469-477: Extract the duplicated timed LLM request logic from
process and processCommandTransform into a shared helper such as
timedLLMRequest(_:), using LLMAPITransport.data(for:) internally and logging
elapsed time with the model. Replace both existing t0/os_log blocks with calls
to this helper, preserving their returned data and response behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9a9127a5-9226-4844-b78b-a25afc5644ca
📒 Files selected for processing (5)
CHANGELOG.mdSources/AppState.swiftSources/LLMAPITransport.swiftSources/PostProcessingService.swiftSources/TranscriptionService.swift
Split out from #254. Stacked on #263 (the timeout fix) since both touch
LLMAPITransportsession handling — this diff will show the timeout changes too until #263 merges, then it reduces to just the prewarming + instrumentation.Prewarm API connections while recording
Transcription uploads deliberately use a fresh URLSession per upload, which put a full DNS + TCP + TLS handshake (~100–300 ms) on the critical stop-to-paste path of every dictation. A one-shot session is now created and its connection opened when recording starts; the next upload to the same host consumes it — still exactly one session per upload, so the poisoning defense is unchanged. The shared data session is warmed the same way for post-processing/context requests.
Latency instrumentation
Transcription uploads and LLM requests now log durations to the existing
com.zachlatta.freeflowos_log subsystem, so pipeline latency is visible in Console.🤖 Generated with Claude Code
Summary by CodeRabbit
Improvements
Bug Fixes