Skip to content

feat(local-ai): add editable Ollama server URL with connection test#2210

Open
M3gA-Mind wants to merge 7 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/ollama-external-server-url
Open

feat(local-ai): add editable Ollama server URL with connection test#2210
M3gA-Mind wants to merge 7 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/ollama-external-server-url

Conversation

@M3gA-Mind
Copy link
Copy Markdown
Contributor

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

Summary

  • ollama_base_url() previously read only env vars and ignored config.local_ai.base_url; adds ollama_base_url_from_config(config) with correct priority chain (config → env → default) and updates bootstrap/health-check/infer/vision-embed callsites
  • Registers new RPC openhuman.local_ai_test_connection that probes /api/tags with a 3-second timeout and returns {reachable, error, models_count}
  • Adds an editable "Ollama Server URL" section to LocalModelDebugPanel / ModelStatusSection: text input, inline validation, Test Connection button, Save/Reset, seeded from persisted config on mount
  • Adds validate_ollama_url in both Rust and TypeScript (scheme + host + creds + path normalization)
  • Adds i18n keys (localModel.ollamaServer.*) across all 12 locale chunks

Problem

  • Windows users running Ollama on an existing server/port had no UI way to point OpenHuman at it — the only option was hand-editing config.toml
  • ollama_base_url() ignored config.local_ai.base_url entirely, so the config field was effectively a no-op
  • The bootstrap flow attempted binary discovery/install even when an external server was reachable; once ollama_healthy() uses the config-aware URL this path is skipped naturally

Solution

  • ollama_base_url_from_config(config) replaces the env-var-only lookup in all bootstrap-critical callsites; ollama_base_url() zero-arg wrapper preserved for non-critical legacy callsites
  • ensure_ollama_server is now purely a connectivity check — if the configured endpoint responds, bootstrap proceeds without any binary discovery or install attempt
  • Frontend seeds URL state from openhumanGetConfig() on mount so the saved value is visible immediately, not only after running diagnostics
  • Two-step UX: Test Connection (non-destructive probe) then explicit Save

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy — 4 Rust unit tests for test_ollama_connection, 8 Vitest tests for ModelStatusSection URL UX, 12 tests for ollamaUrlValidation, 8 Rust tests for validate_ollama_url + ollama_base_url_from_config
  • Diff coverage ≥ 80% — all new logic paths covered; 2627/2630 Vitest tests pass; Rust tests pass
  • N/A: Coverage matrix updated — no new feature rows; this wires an existing config field to UI
  • N/A: All affected feature IDs listed — behaviour-only config wiring, no new feature IDs
  • No new external network dependencies introduced — test_ollama_connection uses the existing reqwest client already in scope; mock Axum server used in Rust tests
  • N/A: Manual smoke checklist updated — Local Model debug panel; no release-cut surface change
  • Linked issue closed via Closes #2159 in Related section

Impact

  • Desktop only (Local Model settings panel in Tauri shell)
  • No performance or security implications; the connection test is a read-only GET with a 3-second timeout
  • ollama_base_url() zero-arg form unchanged — non-critical callsites (embeddings, memory, triage) are unaffected

Related

Closes #2159


AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

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

Commit & Branch

  • Branch: feat/ollama-external-server-url
  • Commit SHA: 2011507

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck
  • Focused tests: pnpm debug unit — 2627 passed, 0 failed; cargo test -p openhuman test_ollama_connection — 4 passed
  • Rust fmt/check (if changed): cargo check --manifest-path Cargo.toml clean
  • Tauri fmt/check (if changed): cargo check --manifest-path app/src-tauri/Cargo.toml clean

Validation Blocked

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

Behavior Changes

  • Intended behavior change: Users can now configure, test, and save an external Ollama server URL from the Local Model debug panel; the saved URL is used by all local-AI inference paths
  • User-visible effect: New "Ollama Server URL" section in Settings → Developer → Local Model with editable input, Test Connection button, and Save/Reset

Parity Contract

  • Legacy behavior preserved: ollama_base_url() zero-arg unchanged; env var OPENHUMAN_OLLAMA_BASE_URL still overrides default when config field is unset
  • Guard/fallback/dispatch parity checks: Bootstrap health check now uses config-aware URL; fallback to env var → default preserved when config.local_ai.base_url is None

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: this one
  • Resolution (closed/superseded/updated): N/A

Summary by CodeRabbit

  • New Features

    • Configurable Ollama server URL in settings with Test/Save/Reset controls; input seeded from app config and shows reachability/model info and validation feedback.
    • Normalization/validation of Ollama URLs (http/https, host/port rules).
  • Bug Fixes / Behavior

    • File tooling now uses stricter, canonical path validation—produces clearer failure messages for invalid or disallowed paths.
  • Documentation

    • Expanded developer guidance with “Fixes & Gotchas”, workflow checklist, testing caveats (intermittent coverage race), and repo/CI tips.
  • Tests

    • Added/extended tests for URL validation, connection testing, and related UI flows.

Review Change Stack

M3gA-Mind added 5 commits May 18, 2026 18:50
Merges the split is_path_allowed/is_resolved_path_allowed API into a
single validate_path() that resolves symlinks before checking workspace
boundaries and forbidden paths. Adds validate_parent_path() for write
operations where the target file may not yet exist.

Two callers (image_info.rs, cron/scheduler.rs) were missing the
resolved check entirely — image_info.rs could be used to exfiltrate
files via a symlink inside the workspace.

Closes tinyhumansai#1927
…te before create_dir_all

- In validate_path/validate_parent_path, switch from string starts_with to
  path-component-aware comparison for forbidden_paths.
- Resolve relative forbidden entries against the workspace root so entries
  like "secrets" correctly block workspace/secrets/ even after canonicalization.
- Skip absolute forbidden entries that are ancestors of the workspace root
  (e.g. /tmp when workspace is /tmp/…) — the workspace containment check
  already guarantees the resolved path is safe.
- validate_parent_path now walks up to the deepest existing ancestor before
  canonicalizing, so it works without requiring the parent directory to exist.
- file_write and csv_export now call validate_parent_path BEFORE create_dir_all,
  then create directories at the validated canonical location. This prevents a
  symlinked path component from causing directory creation outside the workspace
  before the security check fires.

Fixes 25 failing filesystem tests (false-positive forbidden-path rejections
when workspace is under /tmp) and closes the pre-create-dir_all attack surface.
…forbidden-entry symlink regression test

- image_info.rs: remove redundant "Path not allowed: " prefix — validate_path
  already returns a complete user-facing error string.
- policy_tests.rs: add validate_path_blocks_symlink_to_relative_forbidden_entry
  to lock in the be29669 fix where relative forbidden entries (e.g. "secrets")
  were not resolved against the workspace root and could be bypassed via a
  symlink pointing into the forbidden directory.
Lines 888-896 of policy.rs were uncovered — the forbidden_paths loop
inside validate_parent_path had no test. Add
validate_parent_path_blocks_forbidden_path to assert that writing a new
file into a relative-forbidden directory is rejected.
Wires config.local_ai.base_url into the UI for the first time: previously
the field existed in core but was never written from the frontend and was
ignored by ollama_base_url() which only read env vars.

- Add ollama_base_url_from_config(config) with priority chain:
  config.local_ai.base_url > env vars > default; update bootstrap/health
  check/infer/vision-embed callsites so an external server is used when
  configured without requiring a local binary
- Add validate_ollama_url() (Rust + TS mirrors) for scheme/host/creds
  validation with path normalisation
- Register openhuman.local_ai_test_connection RPC that probes /api/tags
  with a 3-second timeout and returns {reachable, error, models_count}
- Add Ollama Server URL section to LocalModelDebugPanel/ModelStatusSection:
  editable input, inline validation, Test Connection button, Save/Reset,
  seeds from persisted config on mount via openhumanGetConfig()
- Add i18n keys (localModel.ollamaServer.*) across all locale chunks
- 4 new Rust tests for test_ollama_connection; 8 new Vitest tests for
  URL input UX; 12 tests for ollamaUrlValidation util

Closes tinyhumansai#2159
@M3gA-Mind M3gA-Mind requested a review from a team May 19, 2026 12:19
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c05be309-1232-4859-a08f-52a2b19f522d

📥 Commits

Reviewing files that changed from the base of the PR and between fba0d51 and 0a8bf1d.

📒 Files selected for processing (4)
  • app/src/components/settings/panels/__tests__/LocalModelDebugPanel.test.tsx
  • app/src/components/settings/panels/local-model/ModelStatusSection.test.tsx
  • app/src/utils/ollamaUrlValidation.test.ts
  • app/src/utils/tauriCommands/localAi.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/utils/ollamaUrlValidation.test.ts
  • app/src/components/settings/panels/local-model/ModelStatusSection.test.tsx

📝 Walkthrough

Walkthrough

Adds an editable Ollama Server URL workflow (client validation, UI Test/Save/Reset, i18n, frontend tests, tauri RPC) and server-side support (config-aware base-url selection, validation, connectivity probe). Simultaneously refactors SecurityPolicy to canonicalize/validate paths and updates filesystem tools to use the new async validators.

Changes

External Ollama Server Configuration

Layer / File(s) Summary
URL validation utilities (client & server)
app/src/utils/ollamaUrlValidation.ts, app/src/utils/ollamaUrlValidation.test.ts, src/openhuman/inference/local/ollama.rs
Client validateOllamaUrl() and Rust validate_ollama_url() enforce http/https, strip path/credentials/fragments, normalize to scheme://host[:port]; ollama_base_url_from_config() prefers config.local_ai.base_url over env/default.
Renderer↔Core RPC wrapper
app/src/utils/tauriCommands/localAi.ts, app/src/utils/tauriCommands/localAi.test.ts
Adds OllamaConnectionTestResult type and openhumanLocalAiTestConnection(url) wrapper with tests verifying RPC invocation and error propagation.
RPC schema & handler
src/openhuman/inference/local/schemas.rs, src/openhuman/inference/local/schemas_tests.rs
Adds local_ai_test_connection controller schema/params and handler that calls backend connectivity test and returns JSON; test registry updated.
Ollama admin/service
src/openhuman/inference/local/service/ollama_admin.rs, src/openhuman/inference/local/service/ollama_admin_tests.rs, src/openhuman/inference/local/service/mod.rs
Refactors admin helpers to accept explicit base URLs, adds ollama_healthy_at, list_models_at, has_model_at, test_ollama_connection() that validates/normalizes a URL and reports reachability and model count; tests adapted to mock endpoints.
Inference/embed integration
src/openhuman/inference/local/service/public_infer.rs, src/openhuman/inference/local/service/vision_embed.rs
Chat/inference/embed now derive Ollama base URL from Config and use URL-aware error formatting (redacts userinfo, strips query/fragment); logs chosen base URL.
Frontend UI and state
app/src/components/settings/panels/LocalModelDebugPanel.tsx, app/src/components/settings/panels/local-model/ModelStatusSection.tsx
Adds editable Ollama Server URL input with validation, Test/Save/Reset flows, seeding from Rust config on mount, connection testing via RPC, and wiring of new props/handlers.
Frontend tests
app/src/components/settings/panels/__tests__/LocalModelDebugPanel.test.tsx, app/src/components/settings/panels/local-model/ModelStatusSection.test.tsx
Adds tests for initialization/seeding from config, Test/Save/Reset UX and RPC calls, validation messages, and connection-result rendering.
Internationalization
app/src/lib/i18n/en.ts, app/src/lib/i18n/chunks/{ar,bn,en,es,fr,hi,id,it,pt,ru,zh-CN}-2.ts
Adds localModel.ollamaServer.* translation keys (helper, label, placeholder, reachability labels, buttons, validation error) across language chunks.
Developer docs
.claude/memory.md
Documents Ollama URL gotchas (config-aware resolution, UI seeding), a flaky coverage tool caveat, project/git tips, workflow gate checklist, and security policy validation entry points.

Security Policy Path Validation Refactor

Layer / File(s) Summary
Core security policy API
src/openhuman/security/policy.rs
Replaces is_path_allowed() with is_path_string_allowed() (string-only checks), adds expand_tilde(), and new async validate_path() and validate_parent_path() that canonicalize targets/ancestors, verify workspace containment, and enforce forbidden-directory blocking.
Security policy tests
src/openhuman/security/policy_tests.rs
Updates assertions to use is_path_string_allowed() and adds async #[tokio::test] coverage for validate_path() and validate_parent_path() including symlink escape and forbidden-path canonicalization checks.
Filesystem tools integration
src/openhuman/tools/impl/filesystem/{file_read,file_write,list_files,grep,edit_file,apply_patch,csv_export}.rs, src/openhuman/tools/impl/browser/image_info.rs
All tools now call validate_path() / validate_parent_path() for resolved/canonical path validation and use returned canonical paths for filesystem operations; errors returned as ToolResult::error.
Scheduler gate update
src/openhuman/cron/scheduler.rs
Switched forbidden_path_argument() check from is_path_allowed to is_path_string_allowed for CLI-argument path-like detection.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ModelStatusSection
  participant LocalModelDebugPanel
  participant RPC as openhumanLocalAiTestConnection
  participant Core as Rust Backend
  User->>ModelStatusSection: Type Ollama URL
  ModelStatusSection->>ModelStatusSection: validateOllamaUrl()
  ModelStatusSection->>User: Show validation error or enable Test/Save
  User->>ModelStatusSection: Click "Test Connection"
  ModelStatusSection->>LocalModelDebugPanel: onTestConnection()
  LocalModelDebugPanel->>RPC: openhumanLocalAiTestConnection(url)
  RPC->>Core: call openhuman.local_ai_test_connection
  Core->>Core: test_ollama_connection() validates & probes /api/tags
  Core-->>RPC: JSON {reachable, models_count, error}
  RPC-->>LocalModelDebugPanel: OllamaConnectionTestResult
  LocalModelDebugPanel->>ModelStatusSection: connectionTestResult prop
  ModelStatusSection->>User: Display "Reachable (N models)" or error
  User->>ModelStatusSection: Click "Save"
  ModelStatusSection->>LocalModelDebugPanel: onSaveOllamaBaseUrl()
  LocalModelDebugPanel->>RPC: openhumanUpdateLocalAiSettings({base_url})
  RPC->>Core: update config.local_ai.base_url
  Core->>Core: persist to config.toml
  Core-->>RPC: success
  RPC-->>LocalModelDebugPanel: update savedOllamaBaseUrl state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • senamakel

Poem

🐰 A rabbit hops, URL in paw,

Tests the Ollama, checks the law,
Paths are canonical, safe and neat,
Configs saved, tests pass sweet,
Hooray — the server greets with models to meet!

🚥 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 PR title 'feat(local-ai): add editable Ollama server URL with connection test' clearly and concisely describes the main change—adding an editable UI field for configuring Ollama server URL with connection testing capability.
Linked Issues check ✅ Passed The PR comprehensively implements all coding requirements from issue #2159: editable URL field wired to config.local_ai.base_url, client/server-side URL validation and normalization, connection test API, config persistence, skip-bootstrap when external server is reachable, and extensive test coverage (Vitest + Rust tests).
Out of Scope Changes check ✅ Passed All changes align with the PR objectives: Ollama URL editing/validation, connection testing, config integration, UI components, i18n, and comprehensive test coverage. Security policy refactoring (#2190) and tool updates support the main path-validation improvements needed for the feature.
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

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@coderabbitai coderabbitai Bot added the feature Net-new user-facing capability or product behavior. label May 19, 2026
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.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/openhuman/inference/local/service/vision_embed.rs (1)

71-76: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid logging raw configured Ollama URLs.

Lines 71-76/83-87/93-96/103-107/159-160 emit base/url directly. With external URLs, userinfo credentials can be exposed in logs.

Use the same redaction approach used in public_infer.rs before logging URL fields.

As per coding guidelines: "Never log secrets, raw JWTs, API keys, or full PII — redact or omit sensitive fields."

Also applies to: 83-87, 93-96, 103-107, 159-160

🤖 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 `@src/openhuman/inference/local/service/vision_embed.rs` around lines 71 - 76,
The tracing::debug calls in
src/openhuman/inference/local/service/vision_embed.rs log raw URL variables
(base and url) which may contain sensitive userinfo; update those debug
statements (the tracing::debug invocations referencing %base and %url and using
body.model/body.prompt) to redact or omit credentials before logging by reusing
the same URL-redaction approach implemented in public_infer.rs (call the
existing redaction helper used there or parse the URL and strip
username/password/authority info), then log the sanitized URL string instead of
the raw base/url.
src/openhuman/security/policy.rs (1)

722-755: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Block Windows %5c traversal and ~\ home paths here too.

Line 723 only expands ~/, and Line 748 only rejects ..%2f / %2f... On Windows, inputs like ~\.ssh\id_rsa and ..%5c..%5csecret.txt still pass is_path_string_allowed(), so the new string-only policy can be bypassed there.

Suggested hardening
 fn expand_tilde(&self, path: &str) -> String {
-    if let Some(rest) = path.strip_prefix("~/") {
+    if let Some(rest) = path
+        .strip_prefix("~/")
+        .or_else(|| path.strip_prefix("~\\"))
+    {
         if let Some(home) = dirs::home_dir() {
-            return format!("{}/{rest}", home.display());
+            return home.join(rest).to_string_lossy().into_owned();
         }
     }
     path.to_string()
 }
 
 // Block URL-encoded traversal attempts (e.g. ..%2f)
 let lower = path.to_lowercase();
- if lower.contains("..%2f") || lower.contains("%2f..") {
+ if lower.contains("..%2f")
+     || lower.contains("%2f..")
+     || lower.contains("..%5c")
+     || lower.contains("%5c..")
+ {
     return false;
 }
🤖 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 `@src/openhuman/security/policy.rs` around lines 722 - 755, The tilde-expansion
and traversal checks in expand_tilde and is_path_string_allowed miss
Windows-style backslashes and URL-encoded backslashes; update expand_tilde to
also match and expand "~\\" and "~\" variants (so "~\\..." and "~\..." resolve
to the home dir) and update is_path_string_allowed to reject URL-encoded
backslashes ("%5c" / "%5C") and encoded traversal patterns such as "..%5c",
"%5c.." (in addition to "..%2f" / "%2f.."), and to normalize/check the
lowercased path for "%5c" occurrences; reference the expand_tilde method for
tilde handling and is_path_string_allowed for adding the extra
encoded-backslash/traversal checks.
src/openhuman/inference/local/service/ollama_admin.rs (1)

1163-1171: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Change /api/tags probe from POST to GET.

Line 1166 uses POST {base_url}/api/tags, but Ollama's /api/tags endpoint supports only GET (and HEAD). Using POST will fail even when the Ollama service is healthy, incorrectly blocking startup.

Fix
         let resp = self
             .http
-            .post(format!("{base_url}/api/tags"))
+            .get(format!("{base_url}/api/tags"))
             .timeout(std::time::Duration::from_secs(3))
             .send()
             .await;
🤖 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 `@src/openhuman/inference/local/service/ollama_admin.rs` around lines 1163 -
1171, In ollama_runner_ok_at change the probe request from a POST to a GET (or
HEAD) against the /api/tags endpoint so the health check uses an HTTP method the
Ollama server supports; update the call in ollama_runner_ok_at (currently using
self.http.post(format!("{base_url}/api/tags"))) to use self.http.get(...) and
keep the timeout, send().await, and the same success/status check logic intact.
src/openhuman/tools/impl/filesystem/edit_file.rs (1)

100-117: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reinstate a raw path gate before symlink_metadata().

After removing the old early path check, workspace_dir.join(path) now lets absolute inputs pass straight into symlink_metadata(). A request like /etc/localtime can return a symlink-specific error before sandbox validation runs, which leaks forbidden-path metadata. Reject disallowed path strings before any filesystem access here.

Suggested fix
+        if !self.security.is_path_string_allowed(path) {
+            return Ok(ToolResult::error(format!("path not allowed: {path}")));
+        }
+
         let full = self.security.workspace_dir.join(path);
 
         // Symlink check must happen on the *unresolved* path —
         // `canonicalize` resolves symlinks, so checking after that point
         // would always see the link's final target.
🤖 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 `@src/openhuman/tools/impl/filesystem/edit_file.rs` around lines 100 - 117,
Before calling tokio::fs::symlink_metadata(&full).await, add a raw-path string
gate that rejects disallowed path strings without touching the filesystem: call
a new or existing string-only validator (e.g.
self.security.validate_path_string(path) or implement that helper) to reject
absolute paths, path traversal, or other disallowed patterns and return
ToolResult::error on failure; keep the existing symlink_metadata check and the
later self.security.validate_path(path).await (which resolves symlinks)
unchanged so no filesystem access occurs for rejected raw paths.
🧹 Nitpick comments (1)
src/openhuman/security/policy.rs (1)

777-905: ⚡ Quick win

Add debug/trace logs on the reject branches in these validators.

These methods only log the success path today. The early Err branches are the ones operators will need when canonicalization, workspace-containment, or forbidden-path checks fail.

As per coding guidelines, "Use log / tracing at debug or trace level on RPC entry and exit, error paths, state transitions, and any branch that is hard to infer from tests alone."

🤖 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 `@src/openhuman/security/policy.rs` around lines 777 - 905, Add debug/trace
logging on all early-return error branches in validate_path and
validate_parent_path so failures are visible; for each Err return (e.g., when
is_path_string_allowed(path) fails, canonicalize/map_err failures,
is_resolved_path_allowed checks, parent/file_name missing, and forbidden_paths
rejections) emit a log::debug or log::trace including the function name
(validate_path or validate_parent_path), the input path, any resolved/canonical
value (resolved, canonical_ancestor, resolved_parent, result), and the error or
reason string before returning the Err; place these logs immediately before each
return Err to show context for workspace_root, forbidden_paths and
canonicalization failures so operators can see why the validator rejected the
path.
🤖 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.

Inline comments:
In @.claude/memory.md:
- Line 154: The notes conflict about test commands: Line 154 suggests using
`pnpm debug unit` for local iteration, while Line 213 mandates `pnpm
test:coverage` before commit; resolve by clarifying scope and guidance—update
the memory.md entries so the `pnpm debug unit` recommendation is explicitly
marked as a local/faster iteration workflow and `pnpm test:coverage` is
described as the CI/pre-commit validation step (or remove the pre-commit
requirement if you intend only local use). Mention both commands by name (`pnpm
debug unit`, `pnpm test:coverage`), why/when to use each, and make the
pre-commit policy consistent with the repository’s gating rules.

In `@app/src/components/settings/panels/local-model/ModelStatusSection.tsx`:
- Line 114: The "Test Connection" button enables RPC calls even for invalid URLs
because canTest only checks ollamaBaseUrlInput and isTestingConnection; update
the canTest computation to also require urlValidation.valid so it becomes true
only when ollamaBaseUrlInput.trim().length > 0, isTestingConnection is false,
and urlValidation.valid is true (referencing the canTest constant and
urlValidation variable in ModelStatusSection.tsx).
- Around line 203-204: The model-count suffix is hardcoded in English; update
the status string assembly where connectionTestResult is used (the ternary
producing `${...} (${connectionTestResult.models_count} models)`) to use the
i18n translator `t` with a pluralized/localized key (e.g. call
`t('localModel.modelsCount', { count: connectionTestResult.models_count })` or
similar) so the count and "models" suffix are localized; keep the existing keys
`localModel.ollamaServer.reachable`/`unreachable` and include the translated
count fragment conditionally when connectionTestResult.models_count is a number.

In `@app/src/components/settings/panels/LocalModelDebugPanel.tsx`:
- Line 351: The current log call writes the raw ollamaBaseUrlInput (in
LocalModelDebugPanel.tsx) which can include userinfo and leak credentials;
update the save/log path that calls log(...) so it redacts or omits sensitive
userinfo before logging (e.g., parse ollamaBaseUrlInput, strip username/password
or replace them with "[REDACTED]" and log only the scheme+host+path or a masked
URL), referencing the ollamaBaseUrlInput variable and the log(...) invocation in
the save handler to ensure no raw secrets are emitted to frontend logs.
- Around line 318-321: The code unconditionally sets both setOllamaBaseUrlInput
and setSavedOllamaBaseUrl from result.ollama_base_url, which overwrites any
in-progress user edits; change it so only the saved state is always updated
(call setSavedOllamaBaseUrl(result.ollama_base_url)) but only update the input
field via setOllamaBaseUrlInput when the current ollamaBaseUrlInput is empty or
matches the previous saved value (i.e., no unsaved edits). Use the existing
state variables ollamaBaseUrlInput and savedOllamaBaseUrl to decide whether to
call setOllamaBaseUrlInput.

In `@app/src/lib/i18n/chunks/ar-2.ts`:
- Around line 242-250: The Arabic locale file contains untranslated English
strings for the ollamaServer keys; translate the entries
'localModel.ollamaServer.helperText', 'localModel.ollamaServer.label',
'localModel.ollamaServer.placeholder', 'localModel.ollamaServer.reachable',
'localModel.ollamaServer.resetButton', 'localModel.ollamaServer.saveButton',
'localModel.ollamaServer.testButton', 'localModel.ollamaServer.unreachable', and
'localModel.ollamaServer.validationError' into Arabic, replacing the English
values with proper Arabic text while keeping the keys unchanged (e.g., provide
Arabic equivalents for "Example: http://192.168.1.5:11434", "Ollama Server URL",
"http://localhost:11434", "Reachable", "Reset to default", "Save", "Test
Connection", "Unreachable", and "Must be a valid http:// or https:// URL").

In `@app/src/lib/i18n/chunks/bn-2.ts`:
- Around line 251-259: The new Bengali locale file contains untranslated English
strings for the Ollama keys; update all keys under localModel.ollamaServer
(localModel.ollamaServer.helperText, .label, .placeholder, .reachable,
.resetButton, .saveButton, .testButton, .unreachable, .validationError) with
proper Bengali translations so the UI remains consistent for bn locale—replace
each English value with the correct Bengali phrase, keeping placeholders and URL
examples intact (e.g., keep http:// or https:// in validationError and example
URLs in helperText/placeholder) and ensure punctuation/formatting matches other
bn entries.

In `@app/src/lib/i18n/chunks/es-2.ts`:
- Around line 254-262: The Spanish locale entries for the
localModel.ollamaServer keys are still in English; update the values for
'localModel.ollamaServer.helperText', 'localModel.ollamaServer.label',
'localModel.ollamaServer.placeholder', 'localModel.ollamaServer.reachable',
'localModel.ollamaServer.resetButton', 'localModel.ollamaServer.saveButton',
'localModel.ollamaServer.testButton', 'localModel.ollamaServer.unreachable', and
'localModel.ollamaServer.validationError' to correct Spanish translations (keep
the example URL format in helperText/placeholder but translate surrounding text
and UI button/state labels and the validation message).

In `@app/src/lib/i18n/chunks/fr-2.ts`:
- Around line 256-264: Replace the English strings for the i18n keys
'localModel.ollamaServer.helperText', 'localModel.ollamaServer.label',
'localModel.ollamaServer.placeholder', 'localModel.ollamaServer.reachable',
'localModel.ollamaServer.resetButton', 'localModel.ollamaServer.saveButton',
'localModel.ollamaServer.testButton', 'localModel.ollamaServer.unreachable', and
'localModel.ollamaServer.validationError' with proper French translations (e.g.,
helperText -> "Exemple : http://192.168.1.5:11434", label -> "URL du serveur
Ollama", placeholder -> "http://localhost:11434", reachable -> "Accessible",
resetButton -> "Réinitialiser par défaut", saveButton -> "Enregistrer",
testButton -> "Tester la connexion", unreachable -> "Inaccessible",
validationError -> "Doit être une URL valide commençant par http:// ou
https://"), ensuring the keys remain unchanged.

In `@app/src/lib/i18n/chunks/hi-2.ts`:
- Around line 249-257: The Hindi i18n chunk contains English strings for the new
keys (localModel.ollamaServer.helperText, localModel.ollamaServer.label,
localModel.ollamaServer.placeholder, localModel.ollamaServer.reachable,
localModel.ollamaServer.resetButton, localModel.ollamaServer.saveButton,
localModel.ollamaServer.testButton, localModel.ollamaServer.unreachable,
localModel.ollamaServer.validationError); replace each English value with an
appropriate Hindi translation preserving meaning and any example URL formatting
(e.g., keep "http://..." examples unchanged) so all localModel.ollamaServer.*
entries are fully localized for Hindi users.

In `@app/src/lib/i18n/chunks/id-2.ts`:
- Around line 250-258: The Indonesian locale contains untranslated English
strings for the localModel.ollamaServer keys; replace the values for
'localModel.ollamaServer.helperText', 'localModel.ollamaServer.label',
'localModel.ollamaServer.placeholder', 'localModel.ollamaServer.reachable',
'localModel.ollamaServer.resetButton', 'localModel.ollamaServer.saveButton',
'localModel.ollamaServer.testButton', 'localModel.ollamaServer.unreachable', and
'localModel.ollamaServer.validationError' with proper Indonesian translations
(preserve placeholders like example URLs), ensuring wording matches existing
locale style and pluralization/format conventions.

In `@app/src/lib/i18n/chunks/it-2.ts`:
- Around line 251-259: The new localization entries for the Ollama server are
still in English; update the values for the keys
'localModel.ollamaServer.helperText', 'localModel.ollamaServer.label',
'localModel.ollamaServer.placeholder', 'localModel.ollamaServer.reachable',
'localModel.ollamaServer.resetButton', 'localModel.ollamaServer.saveButton',
'localModel.ollamaServer.testButton', 'localModel.ollamaServer.unreachable', and
'localModel.ollamaServer.validationError' with their Italian equivalents
(translate the helper/example URL text, label, placeholder, status strings,
button labels and validation message) so the it-2.ts chunk is fully localized.

In `@app/src/lib/i18n/chunks/pt-2.ts`:
- Around line 254-262: The Portuguese locale file contains untranslated strings
for the Ollama server UI keys (localModel.ollamaServer.helperText, .label,
.placeholder, .reachable, .resetButton, .saveButton, .testButton, .unreachable,
.validationError); replace each English value with its proper Portuguese
translation (e.g., helperText/example, label, placeholder URL text,
reachable/unreachable statuses, button labels, and the validation message) so
the UI is fully localized and consistent with other pt translations.

In `@app/src/lib/i18n/chunks/ru-2.ts`:
- Around line 249-257: Translate the English strings for the Ollama Server UI
entries in the Russian locale chunk: replace values for keys
'localModel.ollamaServer.helperText', 'localModel.ollamaServer.label',
'localModel.ollamaServer.placeholder', 'localModel.ollamaServer.reachable',
'localModel.ollamaServer.resetButton', 'localModel.ollamaServer.saveButton',
'localModel.ollamaServer.testButton', 'localModel.ollamaServer.unreachable', and
'localModel.ollamaServer.validationError' with proper Russian translations
(e.g., helperText -> "Например: http://192.168.1.5:11434", label -> "URL сервера
Ollama", placeholder -> "http://localhost:11434", reachable -> "Доступен",
resetButton -> "Сбросить по умолчанию", saveButton -> "Сохранить", testButton ->
"Проверить подключение", unreachable -> "Недоступен", validationError -> "Должен
быть корректный URL с http:// или https://"), keeping placeholders and example
URLs intact.

In `@app/src/lib/i18n/chunks/zh-CN-2.ts`:
- Around line 233-241: Translate the English values for the keys
'localModel.ollamaServer.helperText', 'localModel.ollamaServer.label',
'localModel.ollamaServer.placeholder', 'localModel.ollamaServer.reachable',
'localModel.ollamaServer.resetButton', 'localModel.ollamaServer.saveButton',
'localModel.ollamaServer.testButton', 'localModel.ollamaServer.unreachable', and
'localModel.ollamaServer.validationError' into Simplified Chinese in the zh-CN
chunk so the UI is consistent (keep the keys unchanged, only replace the string
values with appropriate Chinese translations for helper text, label,
placeholder, status texts, button labels, and validation message).

In `@src/openhuman/inference/local/service/ollama_admin.rs`:
- Around line 1252-1256: has_model() currently calls ollama_base_url() and
ignores the configured external URL (config.local_ai.base_url), causing
inconsistent model checks; update has_model() and its callers to accept a
resolved base_url (or a &Config reference) and call
has_model_at(&resolved_base_url, model). Thread the resolved base URL from the
higher-level model ensure/pull functions (where config is available) into the
has_model() call sites so has_model_at uses the same base URL used for
pull/ensure operations; adjust signatures for has_model() and any callsites
accordingly and remove direct use of ollama_base_url() inside has_model().

In `@src/openhuman/inference/local/service/public_infer.rs`:
- Around line 567-569: The debug log in inference_with_temperature_internal
prints base_url verbatim which can leak credentials in URL userinfo; update the
logging to redact or omit sensitive userinfo by parsing base_url (e.g., with
Url::parse) and replacing username/password/userinfo with a fixed placeholder
(or log only the scheme+host+path) before passing it to log::debug; ensure you
reference the base_url variable and keep the rest of the log message unchanged.

In `@src/openhuman/tools/impl/filesystem/csv_export.rs`:
- Around line 195-197: Move the SecurityPolicy::record_action() call to occur
before any filesystem probing or creation so the action budget is charged prior
to canonicalization, symlink checks, or tokio::fs::create_dir_all; specifically,
call record_action() before you canonicalize/resolve the target path and before
the block that checks resolved_target.parent() and calls
tokio::fs::create_dir_all(), and ensure the same change is applied to the
surrounding range (lines ~200-214) where symlink existence/canonicalization and
file-existence checks occur.

In `@src/openhuman/tools/impl/filesystem/file_write.rs`:
- Around line 74-76: The code currently creates parent directories and probes
the target before consuming quota; move the SecurityPolicy::record_action() call
to run immediately after validating the target path (before any filesystem
mutations or probing such as tokio::fs::create_dir_all,
resolved_target.parent(), or symlink checks). Specifically, update the
file_write implementation so that is_rate_limited() still guards entry but
record_action() is invoked prior to any use of resolved_target (including the
create_dir_all call and the logic around resolved_target.exists()/symlink
probing in the block covering lines ~74–93), ensuring budget is consumed before
any directory creation or metadata probing.

---

Outside diff comments:
In `@src/openhuman/inference/local/service/ollama_admin.rs`:
- Around line 1163-1171: In ollama_runner_ok_at change the probe request from a
POST to a GET (or HEAD) against the /api/tags endpoint so the health check uses
an HTTP method the Ollama server supports; update the call in
ollama_runner_ok_at (currently using
self.http.post(format!("{base_url}/api/tags"))) to use self.http.get(...) and
keep the timeout, send().await, and the same success/status check logic intact.

In `@src/openhuman/inference/local/service/vision_embed.rs`:
- Around line 71-76: The tracing::debug calls in
src/openhuman/inference/local/service/vision_embed.rs log raw URL variables
(base and url) which may contain sensitive userinfo; update those debug
statements (the tracing::debug invocations referencing %base and %url and using
body.model/body.prompt) to redact or omit credentials before logging by reusing
the same URL-redaction approach implemented in public_infer.rs (call the
existing redaction helper used there or parse the URL and strip
username/password/authority info), then log the sanitized URL string instead of
the raw base/url.

In `@src/openhuman/security/policy.rs`:
- Around line 722-755: The tilde-expansion and traversal checks in expand_tilde
and is_path_string_allowed miss Windows-style backslashes and URL-encoded
backslashes; update expand_tilde to also match and expand "~\\" and "~\"
variants (so "~\\..." and "~\..." resolve to the home dir) and update
is_path_string_allowed to reject URL-encoded backslashes ("%5c" / "%5C") and
encoded traversal patterns such as "..%5c", "%5c.." (in addition to "..%2f" /
"%2f.."), and to normalize/check the lowercased path for "%5c" occurrences;
reference the expand_tilde method for tilde handling and is_path_string_allowed
for adding the extra encoded-backslash/traversal checks.

In `@src/openhuman/tools/impl/filesystem/edit_file.rs`:
- Around line 100-117: Before calling tokio::fs::symlink_metadata(&full).await,
add a raw-path string gate that rejects disallowed path strings without touching
the filesystem: call a new or existing string-only validator (e.g.
self.security.validate_path_string(path) or implement that helper) to reject
absolute paths, path traversal, or other disallowed patterns and return
ToolResult::error on failure; keep the existing symlink_metadata check and the
later self.security.validate_path(path).await (which resolves symlinks)
unchanged so no filesystem access occurs for rejected raw paths.

---

Nitpick comments:
In `@src/openhuman/security/policy.rs`:
- Around line 777-905: Add debug/trace logging on all early-return error
branches in validate_path and validate_parent_path so failures are visible; for
each Err return (e.g., when is_path_string_allowed(path) fails,
canonicalize/map_err failures, is_resolved_path_allowed checks, parent/file_name
missing, and forbidden_paths rejections) emit a log::debug or log::trace
including the function name (validate_path or validate_parent_path), the input
path, any resolved/canonical value (resolved, canonical_ancestor,
resolved_parent, result), and the error or reason string before returning the
Err; place these logs immediately before each return Err to show context for
workspace_root, forbidden_paths and canonicalization failures so operators can
see why the validator rejected the path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 81a5b35e-b1e1-4f6b-9283-2475ffdb640e

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa2984 and 2011507.

📒 Files selected for processing (38)
  • .claude/memory.md
  • app/src/components/settings/panels/LocalModelDebugPanel.tsx
  • app/src/components/settings/panels/local-model/ModelStatusSection.test.tsx
  • app/src/components/settings/panels/local-model/ModelStatusSection.tsx
  • app/src/lib/i18n/chunks/ar-2.ts
  • app/src/lib/i18n/chunks/bn-2.ts
  • app/src/lib/i18n/chunks/en-2.ts
  • app/src/lib/i18n/chunks/es-2.ts
  • app/src/lib/i18n/chunks/fr-2.ts
  • app/src/lib/i18n/chunks/hi-2.ts
  • app/src/lib/i18n/chunks/id-2.ts
  • app/src/lib/i18n/chunks/it-2.ts
  • app/src/lib/i18n/chunks/pt-2.ts
  • app/src/lib/i18n/chunks/ru-2.ts
  • app/src/lib/i18n/chunks/zh-CN-2.ts
  • app/src/lib/i18n/en.ts
  • app/src/utils/ollamaUrlValidation.test.ts
  • app/src/utils/ollamaUrlValidation.ts
  • app/src/utils/tauriCommands/localAi.ts
  • src/openhuman/cron/scheduler.rs
  • src/openhuman/inference/local/ollama.rs
  • src/openhuman/inference/local/schemas.rs
  • src/openhuman/inference/local/schemas_tests.rs
  • src/openhuman/inference/local/service/mod.rs
  • src/openhuman/inference/local/service/ollama_admin.rs
  • src/openhuman/inference/local/service/ollama_admin_tests.rs
  • src/openhuman/inference/local/service/public_infer.rs
  • src/openhuman/inference/local/service/vision_embed.rs
  • src/openhuman/security/policy.rs
  • src/openhuman/security/policy_tests.rs
  • src/openhuman/tools/impl/browser/image_info.rs
  • src/openhuman/tools/impl/filesystem/apply_patch.rs
  • src/openhuman/tools/impl/filesystem/csv_export.rs
  • src/openhuman/tools/impl/filesystem/edit_file.rs
  • src/openhuman/tools/impl/filesystem/file_read.rs
  • src/openhuman/tools/impl/filesystem/file_write.rs
  • src/openhuman/tools/impl/filesystem/grep.rs
  • src/openhuman/tools/impl/filesystem/list_files.rs

Comment thread .claude/memory.md
Comment thread app/src/components/settings/panels/local-model/ModelStatusSection.tsx Outdated
Comment thread app/src/components/settings/panels/local-model/ModelStatusSection.tsx Outdated
Comment thread app/src/components/settings/panels/LocalModelDebugPanel.tsx
Comment thread app/src/components/settings/panels/LocalModelDebugPanel.tsx Outdated
Comment thread app/src/lib/i18n/chunks/zh-CN-2.ts Outdated
Comment thread src/openhuman/inference/local/service/ollama_admin.rs
Comment thread src/openhuman/inference/local/service/public_infer.rs
Comment thread src/openhuman/tools/impl/filesystem/csv_export.rs
Comment thread src/openhuman/tools/impl/filesystem/file_write.rs
- Gate Test button on URL validation rather than non-empty input
- Localize model count in connection result display
- Prevent diagnostics poll from overwriting unsaved URL edits
- Redact credentials from Ollama URL in debug log output
- Replace English placeholder translations in all 10 locale chunks
  (ar, bn, es, fr, hi, id, it, pt, ru, zh-CN) including new modelCount key
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 19, 2026
…nd validation

Covers diff-cover missing lines:
- LocalModelDebugPanel: config seed on mount, handleTestConnection (success +
  throw paths), handleSaveOllamaBaseUrl, handleResetOllamaBaseUrl
- ModelStatusSection: onChange input, spinner when testing, reachable/unreachable
  display, validation error rendering
- ollamaUrlValidation: unparseable URL format error (catch branch)
- tauriCommands/localAi: openhumanLocalAiTestConnection RPC dispatch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Let users configure an external Ollama server URL

1 participant