[Feat]: Configurable per-server network request timeout for remote models#778
Conversation
Add tests for the configurable network timeout across the remote-server stack: - openai.ts: timeout normalization (undefined/NaN/<=0/non-finite fall back to defaults; positive finite passes through), raised connection timeout, default-30s connection abort, configured idle abort, idle-timer-resets- per-chunk, and timeout forwarding through fetchModels/testConnection. - OpenAICompletionEngine: forwards the constructed timeoutMs raw. - ServerStore: forwards a saved requestTimeoutMs (and undefined) to fetchModels/testConnection without crashing. - ModelStore.setRemoteModel: builds and rebuilds the engine carrying the server's requestTimeoutMs. - ServerDetailsSheet: timeout input render/prefill, edit-time probe uses the in-edit field with fallback to the saved value, save converts seconds to ms and clears to undefined when empty. - RemoteModelSheet: distinct probe feeds (in-edit field via fetchModelsWithHeaders for manual add vs saved requestTimeoutMs via fetchModels on chip press), plus add-server persistence.
Wrap the long json mock line; use advance-then-await for the two fake-timer connection-timeout probes so the rejection settles after the timer fires (satisfies jest/valid-expect without a deferred await).
Adds Selectors.remoteModel.timeoutInput (remote-timeout-input) and Selectors.serverDetails.timeoutInput (server-details-timeout-input) for the per-server request timeout fields.
Visual evidence — Request timeout (seconds) fieldsCaptured on iOS simulator (iPhone 17 Pro, iOS 26.0) for issue #776. Both new timeout inputs render with label and helper text. Add path —
|
e612502 to
6f1c41b
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds a configurable, per-server network request timeout for remote/OpenAI-compatible model servers, allowing slow cold-start or slow-streaming servers to complete requests without being cut off by the existing fixed timeouts.
Changes:
- Introduces
ServerConfig.requestTimeoutMsand forwards it through stores/engine into OpenAI request helpers. - Adds a single normalization point (
resolveTimeout) insrc/api/openai.tsand applies the configured timeout to both connection and idle guards (while keeping per-chunk idle resets). - Adds UI inputs (add/edit flows) for “Request timeout (seconds)” with seconds↔ms conversion, plus unit/e2e test coverage for wiring and behavior.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/types.ts | Adds requestTimeoutMs to persisted server config shape. |
| src/store/ServerStore.ts | Forwards per-server timeout to model fetch and connection test calls. |
| src/store/ModelStore.ts | Passes server timeout into the remote OpenAI engine construction. |
| src/store/tests/ServerStore.test.ts | Verifies timeout forwarding (and undefined passthrough) for store calls. |
| src/store/tests/ModelStore.test.ts | Verifies engine rebuild/selection carries updated timeout values. |
| src/locales/en.json | Adds i18n strings for the new timeout inputs. |
| src/components/ServerDetailsSheet/ServerDetailsSheet.tsx | Adds edit-flow timeout input, prefill, save, and probe wiring. |
| src/components/ServerDetailsSheet/tests/ServerDetailsSheet.test.tsx | Tests edit-sheet rendering, ms↔s conversion, and probe wiring. |
| src/components/RemoteModelSheet/RemoteModelSheet.tsx | Adds add-flow timeout input, probe wiring, and persistence via addServer. |
| src/components/RemoteModelSheet/tests/RemoteModelSheet.test.tsx | Tests add-flow probe feed vs chip feed and persistence conversions. |
| src/api/openai.ts | Adds resolveTimeout and threads timeout through fetch + streaming helpers. |
| src/api/completionEngines.ts | Extends OpenAICompletionEngine to carry/forward timeout to streaming. |
| src/api/tests/openai.test.ts | Adds coverage for timeout normalization and stream/fetch timeout behavior. |
| src/api/tests/completionEngines.test.ts | Ensures engine forwards constructed timeout argument. |
| e2e/helpers/selectors.ts | Adds e2e selectors for the new timeout inputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
E2E run — all feature specs + quick-smoke (iOS sim + Android emulator)Ran the 12 feature/smoke specs on iPhone 17 Pro simulator (iOS 26.0) and Android emulator-5554 (API 16) against this branch. App built fresh for both (iOS Release-iphonesimulator
The spec this PR actually touches — Why the
|
The palsTab selector was switched to the drawer-item-pals testID so the drawer open/close indicator survives a language switch, but navigateToPals also taps it — and a Paper Drawer.Item does not reliably respond to a testID tap on iOS, so the tap became a no-op and the drawer never closed. Tap the visible Pals label instead (matching the other tabs); keep the testID for the open/close indicator.
tapThinkingToggle offsets the tap to avoid the TTS VoiceChip, but the offset could compute an x just past the toggle's edge (measured x=326 for a 257-322 button), so the synthetic tap missed and the toggle never flipped (thinking.spec 'toggled off' assertion got true). Clamp x to the toggle bounds so the avoidance offset can never land outside the button.
Update — earlier
|
| Spec | iOS | Android |
|---|---|---|
| quick-smoke | ✅ | ✅ |
| remote-server (this PR's surface) | ✅ | ✅ |
| pal-greeting | ✅ | ✅ |
| talent-tool-use | ✅ | ✅ |
| thinking | ✅ | ✅ |
| thinking-pal-override | ✅ | ✅ |
| draft-autosave | ✅ | ✅ |
| context-banner | ✅ | ✅ |
| download-cancel | ✅ | ✅ |
| hub-run | ✅ | ✅ |
| language | ✅ | ✅ |
| purchase-flow | ✅ | n/a (iOS-only spec) |
Every spec now passes on every platform it applies to. The two fixes are test-harness only (no src/ change), so the timeout feature itself is unchanged from the approved review.
Generated by PocketPal Dev Team
- placeholder said 'default 30s' but an empty field keeps two defaults (30s connection / 60s idle); reword to 'leave empty for default' - extract the duplicated parseTimeoutMs into src/utils/timeout.ts, shared by ServerDetailsSheet and RemoteModelSheet
|
Thanks @copilot — addressed in
On the uncontrolled Generated by PocketPal Dev Team |































Summary
Adds a user-configurable network request timeout for remote / OpenAI-compatible model servers. Some self-hosted or remote LLMs have slow cold starts (>180s) or slow generation; the previously hard-coded 30s connection / 60s idle guards would abort those conversations prematurely. Users can now set a per-server timeout (e.g. 600s).
Closes #776.
What changed
ServerConfig.requestTimeoutMs— new optional per-server field. Absent on older persisted configs → falls back to existing defaults; no migration.src/api/openai.ts— all four request helpers (streamChatCompletion,fetchModels,fetchModelsWithHeaders,testConnection) accept an optionaltimeoutMs. A singleresolveTimeouthelper is the only normalizer:undefined/≤0/NaN/ non-finite → the existing default (30s connection, 60s idle). Stores, engine, and UI forward the raw stored value untouched.ServerDetailsSheet) and the add path (RemoteModelSheet); seconds↔ms conversion at the save boundary. Live connection probes honor the timeout: the in-edit field on add/edit, and the saved value on a known-server chip press.ServerStoreremains the sole writer of the field (addServer/updateServer).Tests
timeoutMsand rebuilds on reselect;ServerStoreforwards undefined without crashing.tsc --noEmitand lint clean. No native changes (nopackage.json/ios//android/).Visual evidence
Both new "Request timeout (seconds)" inputs were captured on the iOS simulator (iPhone 17 Pro) and attached as a PR comment:
RemoteModelSheet,remote-timeout-input) — field appears after a probe attempt, with helper text.ServerDetailsSheet,server-details-timeout-input) — empty (default) and populated (600) states.A local OpenAI-compatible mock server (
GET /v1/models) was used so the probe succeeds and a server can be saved to reach the edit sheet. Screenshots are hosted as PR-comment images (no screenshots are committed to the source tree).The "slow server succeeds with a raised timeout" runtime outcome still needs a real reachable slow remote server and remains a manual check.
Generated by PocketPal Dev Team