feat(inference): OpenAI-compatible /v1 router with user-managed API key#2523
Conversation
Expose the /v1 OpenAI-compatible surface as a stable router for local harnesses. The /v1/* endpoints now accept either the per-launch core bearer or a stable user-managed external API key stored in the credentials service. The models list aggregates the routing-configured providers (chat, reasoning, agentic, coding, memory, embeddings, heartbeat, learning, subconscious) plus a sentinel 'openhuman' entry that follows the active routing config. The AI Settings panel grows a new section to set, rotate, and clear the external API key and exposes the base URL plus auth-header guidance.
|
Warning Review limit reached
Your plan currently allows 2 reviews/hour. Refill in 5 minutes and 23 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (20)
📝 WalkthroughWalkthroughThis PR adds external OpenAI-compatible endpoint authentication with a complete feature implementation: frontend settings UI for API key management, backend authentication middleware for ChangesOpenAI-compatible external endpoint support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Move all hard-coded UI text in the new OpenAI-compatible endpoint section through useT()/en.ts. Document the rule in CLAUDE.md and AGENTS.md so every new user-visible string in app/src/** must go through the i18n catalog.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
app/src/components/settings/panels/AIPanel.tsx (1)
2090-2163: 🏗️ Heavy liftExtract the OpenAI-compatible endpoint UI/logic into a separate module.
This file is already far beyond the repository’s preferred source-file size, and this new card + handlers make it harder to maintain and test. Please split this feature into a focused component/hook (for example
OpenAICompatEndpointCard.tsx+ a small state hook) and keepAIPanel.tsxorchestration-only.As per coding guidelines:
**/*.{js,ts,tsx,jsx}: Prefer files ≤ ~500 lines per source file; split modules when growing to maintain readability and single responsibility.Also applies to: 2483-2500
🤖 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/components/settings/panels/AIPanel.tsx` around lines 2090 - 2163, Extract the OpenAI-compatible endpoint UI and related handlers into a new presentational component (e.g., OpenAICompatEndpointCard) plus a small hook (e.g., useOpenAICompatState): move the entire section JSX and its local state interactions (openAiCompatStatus, openAiCompatBusy, setOpenAiCompatDialogOpen, setOpenAiCompatBusy) and side-effect handler clearOpenAICompatEndpointKey into the new files, keep AIPanel.tsx only orchestrating by importing <OpenAICompatEndpointCard /> and passing props or the hook values/dispatchers; ensure the new hook exposes the status, busy flag, open dialog setter, and a clearKey function that wraps clearOpenAICompatEndpointKey and updates status/busy exactly as currently implemented so behavior remains identical.
🤖 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 `@src/core/auth.rs`:
- Around line 265-272: verify_external_inference_bearer currently loads Config
and performs auth-store work even when the supplied bearer is empty; add an
early-return check at the start of verify_external_inference_bearer (e.g., if
supplied.trim().is_empty() { return false; }) so the function short-circuits and
avoids loading Config or performing I/O for blank/missing headers before any
config/auth-store access.
In `@src/openhuman/inference/http/server.rs`:
- Around line 284-297: Normalize any trailing temperature suffix like `@0.7` from
default-model strings before calling push_model: for config.default_model (the
block that reads config.default_model.as_deref()...) and for cloud provider
default_model (inside the loop over config.cloud_providers where
cp.default_model is used), strip a trailing "@<number>" pattern (e.g. regex
"@\\d+(?:\\.\\d+)?$" or equivalent) after trimming the string, then call
push_model with the cleaned ID; ensure you still preserve provider scoping for
cloud entries (format!("{}:{}", cp.slug, cleaned_model)) and only push when the
cleaned model is non-empty.
---
Nitpick comments:
In `@app/src/components/settings/panels/AIPanel.tsx`:
- Around line 2090-2163: Extract the OpenAI-compatible endpoint UI and related
handlers into a new presentational component (e.g., OpenAICompatEndpointCard)
plus a small hook (e.g., useOpenAICompatState): move the entire section JSX and
its local state interactions (openAiCompatStatus, openAiCompatBusy,
setOpenAiCompatDialogOpen, setOpenAiCompatBusy) and side-effect handler
clearOpenAICompatEndpointKey into the new files, keep AIPanel.tsx only
orchestrating by importing <OpenAICompatEndpointCard /> and passing props or the
hook values/dispatchers; ensure the new hook exposes the status, busy flag, open
dialog setter, and a clearKey function that wraps clearOpenAICompatEndpointKey
and updates status/busy exactly as currently implemented so behavior remains
identical.
🪄 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: db99fbf8-788a-49a4-b06b-9f37821a81d9
📒 Files selected for processing (8)
app/src/components/settings/panels/AIPanel.tsxapp/src/components/settings/panels/__tests__/AIPanel.test.tsxapp/src/services/api/__tests__/aiSettingsApi.test.tsapp/src/services/api/aiSettingsApi.tssrc/core/auth.rssrc/openhuman/inference/http/mod.rssrc/openhuman/inference/http/server.rssrc/openhuman/inference/http/tests.rs
|
Pushed follow-up commits 348bd8c + 652be2a routing the new OpenAI-compat panel strings through i18n and adding the rule to CLAUDE.md + AGENTS.md. Pre-push hook bypassed with |
- Short-circuit empty bearers in verify_external_inference_bearer before loading config to avoid per-request I/O on unauthenticated /v1/* traffic. - Normalize @temperature suffixes for config.default_model and cloud_providers[*].default_model in /v1/models, matching the routing-string path so model IDs dedupe consistently.
i18n-coverage gate failed because the new settings.ai.openAiCompat.* keys only landed in en.ts and en-4.ts. Mirror them into every locale's chunk-4 file so each locale aggregates the same key set; non-English locales fall back to the English value until translated.
Coverage gate (diff-cover ≥ 80%) was failing at 50% because the new OpenAI-compat key controls in AIPanel weren't exercised. Add four tests: - Rotate/Clear chrome rendered when has_api_key is true. - Localized "Unavailable" fallback when status resolution rejects. - Clear-key button invokes clearOpenAICompatEndpointKey and flips back to Set key. - Set-key dialog persists the value via setOpenAICompatEndpointKey and flips the panel to Rotate key.
Summary
/v1/*as a stable OpenAI-compatible router for local harnesses./v1/*requests./v1/models, plus a sentinelopenhumanentry that follows active routing.Problem
The
/v1/*OpenAI-compatible surface was only callable from inside the desktop shell because it required the per-launchOPENHUMAN_CORE_TOKEN. Local agent harnesses (CLIs, IDEs, third-party tooling) need a stable bearer that survives core restarts to treat OpenHuman as an OpenAI-compatible router. Additionally,/v1/modelsonly listed cloud provider defaults plus the local Ollama chat model, missing models reachable through the active routing config.Solution
src/core/auth.rs—/v1/*requests now fall back to a stable bearer stored under theexternal-openai-compatprovider in the credentials/auth service. The internal/rpcsurface still requires the per-launch core token.src/openhuman/inference/http/server.rs—models_handlernow de-dupes via aHashSet, includes a sentinelopenhumanmodel, the configureddefault_model, and every distinct provider referenced by the workload routing config.strip_temperature_suffixstrips numeric@0.7style suffixes when constructing model IDs.app/src/services/api/aiSettingsApi.ts— addsloadOpenAICompatEndpointStatus,setOpenAICompatEndpointKey,clearOpenAICompatEndpointKeyover the existing credentials RPC surface.app/src/components/settings/panels/AIPanel.tsx— adds an OpenAI-compatible endpoint card with base URL display, key status badge, and aProviderKeyDialog-backed rotate/clear flow.Submission Checklist
strip_temperature_suffix. If diff-cover flags gaps, follow-up commit will add tests.Impact
/v1/*;/rpcsecurity unchanged.Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:check(will run in CI)pnpm typecheck(will run in CI)Validation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
/v1/*accepts a stable user-managed API key;/v1/modelslists routing-aware providers.Parity Contract
/v1/*for the desktop shell;/rpcunchanged./v1or/v1/...paths; numeric@<temp>suffix stripping leaves non-numeric suffixes alone.Duplicate / Superseded PR Handling
Summary by CodeRabbit
New Features
Tests