fix(ai-settings): filter reserved slugs in loadAISettings and saveAISettings#2184
Conversation
…ettings
Legacy config files can contain a cloud_providers entry with slug
"openhuman" (stored by older app versions). loadAISettings was passing
this through into UI state, and saveAISettings was sending it back to
Rust without filtering — triggering "slug 'openhuman' is reserved and
cannot be used for a custom provider" on every save.
Apply the same reserved-slug filter ('', 'cloud', 'openhuman', 'ollama',
'pid') that the flushCloudProviders effect in AIPanel already had:
- loadAISettings: strip reserved slugs on load so they never enter state
- saveAISettings: strip reserved slugs before building patch.cloud_providers
Fixes tinyhumansai#2183
📝 WalkthroughWalkthroughThis PR fixes a bug where reserved cloud provider slugs ( ChangesCloud provider slug filtering
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src/services/api/aiSettingsApi.ts (1)
199-206: 💤 Low valueConsider extracting the reserved slugs list to a constant.
The array
['', 'cloud', 'openhuman', 'ollama', 'pid']appears twice (lines 200 and 249). Extracting it to a module-level constant would improve maintainability and ensure the filter logic stays synchronized.♻️ Extract reserved slugs to a constant
+// Reserved slugs that cannot be used for custom cloud providers (matches Rust is_slug_reserved). +const RESERVED_SLUGS = ['', 'cloud', 'openhuman', 'ollama', 'pid'] as const; + /** * Loads the full AI settings view by joining:Then use it in both locations:
const cloudProviders: CloudProviderView[] = config.cloud_providers - .filter(p => !['', 'cloud', 'openhuman', 'ollama', 'pid'].includes(p.slug.trim())) + .filter(p => !RESERVED_SLUGS.includes(p.slug.trim())) .map(p => {patch.cloud_providers = next.cloudProviders - .filter(p => !['', 'cloud', 'openhuman', 'ollama', 'pid'].includes(p.slug.trim())) + .filter(p => !RESERVED_SLUGS.includes(p.slug.trim())) .map(({ id, slug, label, endpoint, auth_style }) => ({Also applies to: 248-256
🤖 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 `@app/src/services/api/aiSettingsApi.ts` around lines 199 - 206, The reserved slug list is duplicated; extract the literal array ['','cloud','openhuman','ollama','pid'] into a module-level constant (e.g., RESERVED_PROVIDER_SLUGS) and replace both inline uses in the cloudProviders computation and the other location (lines referenced around where profileProviders, authKeyForSlug and cloudProviders are used) with that constant; ensure the filter calls use RESERVED_PROVIDER_SLUGS.includes(p.slug.trim()) and leave existing logic around authKeyForSlug(p.slug).toLowerCase() and profileProviders.has(...) intact.
🤖 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 `@app/src/services/api/aiSettingsApi.ts`:
- Around line 199-206: The reserved slug list is duplicated; extract the literal
array ['','cloud','openhuman','ollama','pid'] into a module-level constant
(e.g., RESERVED_PROVIDER_SLUGS) and replace both inline uses in the
cloudProviders computation and the other location (lines referenced around where
profileProviders, authKeyForSlug and cloudProviders are used) with that
constant; ensure the filter calls use
RESERVED_PROVIDER_SLUGS.includes(p.slug.trim()) and leave existing logic around
authKeyForSlug(p.slug).toLowerCase() and profileProviders.has(...) intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4d26927f-dddc-4057-84c4-4d5a6599d109
📒 Files selected for processing (1)
app/src/services/api/aiSettingsApi.ts
Summary
'','cloud','openhuman','ollama','pid') are now filtered inloadAISettings(on read) andsaveAISettings(on write) inapp/src/services/api/aiSettingsApi.tsflushCloudProviderseffect inAIPanel.tsxProblem
Legacy config files (written by older app versions) can contain a
cloud_providersentry withslug: "openhuman"— the built-in OpenHuman cloud service was previously stored as a user-configured provider. On load,loadAISettingspassed this entry through into UI state without filtering. When settings changed and the global Save was pressed,saveAISettingssent the fullcloud_providerslist (including the"openhuman"entry) to the Rust core. Rust'sis_slug_reservedvalidation then rejected it:Solution
Apply the same reserved-slug filter (
!['', 'cloud', 'openhuman', 'ollama', 'pid'].includes(p.slug.trim())) in two places:loadAISettings— filterconfig.cloud_providersbefore mapping so reserved slugs never enter the UI'sdraft.cloudProvidersstatesaveAISettings— filternext.cloudProvidersbefore buildingpatch.cloud_providersso reserved slugs are never sent to Rust on the global Save pathThe filter set mirrors
is_slug_reservedinsrc/openhuman/config/schema/cloud_providers.rs.Submission Checklist
loadAISettings/saveAISettingscall paths in the test suite.## Related— N/A: no matrix rows for reserved-slug filteringdocs/RELEASE-MANUAL-SMOKE.mdCloses #NNNin## RelatedImpact
"openhuman"entry in their config will silently have it dropped on next load — they won't lose any functional routing since"openhuman"maps to the built-in default, which is the fallback anywayRelated
AI Authored PR Metadata
Linear Issue
Commit & Branch
fix/reserved-slug-load-save9d3a273a05c6003efe28cf33371bef99968f702bValidation Run
pnpm --filter openhuman-app format:check— cleanpnpm typecheck— cleanpnpm test— all passingValidation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
openhuman,cloud,ollama,pid, empty) are silently dropped fromcloud_providerson load and on saveParity Contract
flushCloudProviderseffect already had this filter — load and save paths now match itDuplicate / Superseded PR Handling
Summary by CodeRabbit