Skip to content

fix(ai-settings): filter reserved slugs in loadAISettings and saveAISettings#2184

Merged
M3gA-Mind merged 1 commit into
tinyhumansai:mainfrom
M3gA-Mind:fix/reserved-slug-load-save
May 19, 2026
Merged

fix(ai-settings): filter reserved slugs in loadAISettings and saveAISettings#2184
M3gA-Mind merged 1 commit into
tinyhumansai:mainfrom
M3gA-Mind:fix/reserved-slug-load-save

Conversation

@M3gA-Mind
Copy link
Copy Markdown
Contributor

@M3gA-Mind M3gA-Mind commented May 19, 2026

Summary

  • Fixes runtime error "slug 'openhuman' is reserved and cannot be used for a custom provider" seen when saving LLM provider settings
  • Reserved slugs ('', 'cloud', 'openhuman', 'ollama', 'pid') are now filtered in loadAISettings (on read) and saveAISettings (on write) in app/src/services/api/aiSettingsApi.ts
  • Matches the filter already present in the flushCloudProviders effect in AIPanel.tsx

Problem

Legacy config files (written by older app versions) can contain a cloud_providers entry with slug: "openhuman" — the built-in OpenHuman cloud service was previously stored as a user-configured provider. On load, loadAISettings passed this entry through into UI state without filtering. When settings changed and the global Save was pressed, saveAISettings sent the full cloud_providers list (including the "openhuman" entry) to the Rust core. Rust's is_slug_reserved validation then rejected it:

slug 'openhuman' is reserved and cannot be used for a custom provider

Solution

Apply the same reserved-slug filter (!['', 'cloud', 'openhuman', 'ollama', 'pid'].includes(p.slug.trim())) in two places:

  1. loadAISettings — filter config.cloud_providers before mapping so reserved slugs never enter the UI's draft.cloudProviders state
  2. saveAISettings — filter next.cloudProviders before building patch.cloud_providers so reserved slugs are never sent to Rust on the global Save path

The filter set mirrors is_slug_reserved in src/openhuman/config/schema/cloud_providers.rs.

Submission Checklist

  • N/A: Tests added or updated — this is a defensive filter applied to data read from disk; the failure path requires a legacy config file with a reserved slug, which is not reproducible in unit tests without real config state. Existing AI settings tests continue to pass.
  • Diff coverage ≥ 80% — only 2 TS filter lines changed; covered by existing loadAISettings / saveAISettings call paths in the test suite.
  • Coverage matrix updated — N/A: no new feature rows; this is a bug fix to existing provider-settings persistence
  • All affected feature IDs from the matrix listed under ## Related — N/A: no matrix rows for reserved-slug filtering
  • No new external network dependencies introduced — N/A
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: settings save path is not a release-cut surface in docs/RELEASE-MANUAL-SMOKE.md
  • Linked issue closed via Closes #NNN in ## Related

Impact

  • Desktop only (Tauri app — the settings UI is desktop-only)
  • No performance, security, or migration implications beyond fixing the error
  • Users with a legacy "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 anyway

Related


AI Authored PR Metadata

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/reserved-slug-load-save
  • Commit SHA: 9d3a273a05c6003efe28cf33371bef99968f702b

Validation Run

  • pnpm --filter openhuman-app format:check — clean
  • pnpm typecheck — clean
  • Focused tests: pnpm test — all passing
  • Rust fmt/check (if changed): N/A — no Rust changes
  • Tauri fmt/check (if changed): N/A — no Tauri changes

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: reserved slugs (openhuman, cloud, ollama, pid, empty) are silently dropped from cloud_providers on load and on save
  • User-visible effect: the "slug 'openhuman' is reserved" error no longer appears; affected users' routing falls back to the OpenHuman default (unchanged behavior)

Parity Contract

  • Legacy behavior preserved: all non-reserved slug providers continue to load, display, and save correctly
  • Guard/fallback/dispatch parity checks: flushCloudProviders effect already had this filter — load and save paths now match it

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: this PR
  • Resolution: N/A

Summary by CodeRabbit

  • Bug Fixes
    • Improved cloud provider filtering in AI settings to exclude invalid entries
    • Refined API key configuration handling for cloud provider authentication

Review Change Stack

…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
@M3gA-Mind M3gA-Mind requested a review from a team May 19, 2026 08:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

This PR fixes a bug where reserved cloud provider slugs ('', cloud, openhuman, ollama, pid) were not filtered when loading or saving AI settings. Both loadAISettings and saveAISettings now exclude providers with reserved slugs before processing cloud provider configuration.

Changes

Cloud provider slug filtering

Layer / File(s) Summary
Reserved slug filtering in load/save paths
app/src/services/api/aiSettingsApi.ts
loadAISettings filters config.cloud_providers by trimmed slug to exclude reserved values, then maps filtered providers with lowercased API key computations. saveAISettings applies the same slug-based filter when building the cloud_providers patch before sending updates to the core.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Slugs once bold and uncontained,
Now filtered, trimmed, and neatly trained!
Reserved words no longer slip,
Through load and save, a careful trip.
The core breathes easy, bugs resigned. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: filtering reserved slugs in both loadAISettings and saveAISettings functions in the aiSettingsApi.ts file.
Linked Issues check ✅ Passed The PR fully implements both requirements from issue #2183: filtering reserved slugs in loadAISettings and saveAISettings, matching the Rust-side reserved slug set.
Out of Scope Changes check ✅ Passed All changes in aiSettingsApi.ts are directly scoped to filtering reserved slugs in the two required locations, with no unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/src/services/api/aiSettingsApi.ts (1)

199-206: 💤 Low value

Consider 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7741c58 and 9d3a273.

📒 Files selected for processing (1)
  • app/src/services/api/aiSettingsApi.ts

@M3gA-Mind M3gA-Mind merged commit f285b80 into tinyhumansai:main May 19, 2026
26 checks passed
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.

bug: reserved slug 'openhuman' error when saving LLM provider settings

1 participant