feat(desktop): configure OpenAI-compatible base URLs - #3798
Conversation
…back Resolve OpenAI-compatible base URL with pure precedence: non-blank OPENAI_COMPAT_BASE_URL (desktop canonical) > non-blank OPENAI_BASE_URL (generic SDK) > https://api.openai.com/v1. Blank/whitespace values fall through so a empty native override cannot block generic fallback or the default. Keeps default …/v1 path joining and is_openai_host Auto→Responses routing intact. Signed-off-by: mr-r0b0t <adam.manning@gmail.com>
Signed-off-by: mr-r0b0t <adam.manning@gmail.com>
tlongwell-block
left a comment
There was a problem hiding this comment.
Reviewed at exact head 9d609993bfdead6f4d73d39b5fd490e586bfae2e in a fresh worktree, git rev-parse HEAD confirmed in the same shell as every run below.
Verified locally at this head:
pnpm test: 3,853 passed, 0 failed (matches PR body)pnpm typecheck+pnpm check(Biome, file-size ratchet, px-text, pubkey guards): cleancargo test -p buzz-agent: 383 unit + integration suites, 0 failed (matches PR body)- Rust resolver precedence, blank/whitespace fall-through, and
is_openai_hostdefault preservation all read correct
Blocking: your own new E2E test fails deterministically — this is the red Desktop Smoke E2E (2) check.
tests/e2e/edit-agent.spec.ts:281 ("openai-compatible base URL field persists and is hidden from Advanced") fails on the reopen assertion at line 342: getByTestId('persona-provider-base-url') — element not found. Reproduced locally at this exact head; it failed all 3 retries on both CI attempts.
Root cause: the e2e mock bridge's update_managed_agent handler (desktop/src/testing/e2eBridge.ts ~line 8176) echoes name/model/systemPrompt/envVars/respondTo into the mock store but ignores provider. On reopen the LLM provider reverts to unset ("Select a provider…" — visible in the failure screenshot), providerOwnsBaseUrlField returns false, and the base-URL field never renders. The real backend does persist provider (agent_config.rs), so this is a mock gap, not a product bug — but the check is red either way.
Minimal fix, verified to turn the test green locally:
// handleUpdateManagedAgent input type:
provider?: string | null;
// handler body:
if (args.input.provider !== undefined) {
agent.provider = args.input.provider;
}(Also update the mock-boundary caveat comment at the top of edit-agent.spec.ts, which currently documents that the handler ignores provider.)
Non-blocking findings:
- ~70 lines of dead exports in
PersonaProviderStructuredEnvFields.tsx:renderPersonaProviderStructuredEnvFieldsandisOpenAiCompatBaseUrlOwnedhave zero callers anywhere in the repo. Notably,AgentInstanceEditDialog.tsxre-implementsisOpenAiCompatBaseUrlOwnedinline (baseUrlPersonaSatisfied) instead of calling it. Either wire them up or delete them. - The README row for
OPENAI_COMPAT_APIwas changed from\|to\\|— an untouched feature-wise line. GitHub renders both identically (verified via the markdown API), so this is pure churn; please revert. - The Rust fallback commit substantially duplicates open PR #3705 (different author). You disclose the overlap, but two open PRs racing on the same lines needs a maintainer call — consider rebasing onto or superseding it explicitly rather than carrying a parallel implementation.
- Several unrelated blank-line deletions in
AgentConfigFields.tsxadd diff noise.
The core design is sound: the base-URL field mirrors the API-key field's layering (local > persona > global > build > file), blank stays backward-compatible, validation is scoped to non-empty values, and hiding structured keys from Advanced prevents duplicate rows. Fix the mock-bridge gap and this is close.
…ndings Addresses @tlongwell-block's review on PR block#3798. Blocking — e2e mock bridge gap (Desktop Smoke E2E (2)): The update_managed_agent handler in e2eBridge.ts echoed name/model/systemPrompt/envVars/respondTo into the mock store but ignored provider. On dialog reopen the LLM provider reverted to unset, providerOwnsBaseUrlField returned false, and the base-URL field never rendered — failing edit-agent.spec.ts:281 at the reopen assertion. The real backend persists provider (agent_config.rs Option<Option<String>> tri-state) and the frontend sends it; this was purely a mock gap. Add provider to the handler input type and echo it into the store. Update the mock-boundary caveat comment in edit-agent.spec.ts accordingly. Non-blocking: - Delete dead renderPersonaProviderStructuredEnvFields export (65 lines, zero callers) from PersonaProviderStructuredEnvFields.tsx. Wire AgentInstanceEditDialog's inline baseUrlPersonaSatisfied duplicate to call the now-shared isOpenAiCompatBaseUrlOwned export. - Revert README OPENAI_COMPAT_API row from \\| back to \| (cosmetic churn on an untouched feature line; GitHub renders both identically). - Restore 3 unrelated blank-line deletions in AgentConfigFields.tsx that added diff noise. Verified locally at 9d60999 + this commit: - edit-agent.spec.ts:281 green (was deterministically red) - pnpm test: 3,853 passed, 0 failed - cargo test -p buzz-agent: 22 passed, 0 failed - tsc --noEmit: clean - biome check: clean Signed-off-by: mr-r0b0t <adam.manning@gmail.com>
Review resolution —
|
| Check | Result |
|---|---|
edit-agent.spec.ts:281 (the red CI check) |
✅ green |
pnpm test (desktop unit) |
✅ 3,853 passed, 0 failed |
cargo test -p buzz-agent |
✅ 22 passed, 0 failed |
tsc --noEmit |
✅ clean |
biome check . |
✅ clean (exit 0) |
Note: file-size ratchet (pnpm check) is red on these files pre-existing at 9d609993 (the merge-base is stale, so the ratchet sees the full growth from your earlier commits). My changes don't add to it — the dead-code removal actually reduces sizes.
Diff: 6 files, +15 / −72.
Push note: the commit is currently on amanning3390/buzz:feat/openai-compat-base-url-ui (SHA 6c5b8893). My am423 credentials aren't in this environment, so I pushed to my other fork. A maintainer with push access to am423/buzz (or me once I auth am423) can fast-forward am423:feat/openai-compat-base-url-ui to 6c5b8893. Until then CI won't re-run against the PR head. Happy to re-push from the am423 account if preferred.
|
handy for custom openai urls. please reject empty/whitespace base urls so we dont silently fall back to api.openai.com |
Summary
Add complete custom OpenAI-compatible endpoint configuration for the native Buzz Agent.
The current desktop flow exposes an API-key field for the OpenAI-compatible provider but does not expose the endpoint location. This PR adds the missing Base URL field and carries it through persistence, inheritance, native configuration, and requests.
Changes
OPENAI_COMPAT_BASE_URLvalue through the existingenv_varsmaps.http://orhttps://URLs and block invalid configuration.OPENAI_COMPAT_BASE_URL>OPENAI_BASE_URL>https://api.openai.com/v1.Validation
Passed:
pnpm test— 3,853 passed, 0 failedpnpm typecheckcargo test -p buzz-agent— 383 unit tests plus integration suites passedjust ciwas rerun after installing the repository-documented Linux/Tauri prerequisites (libatk1.0-dev,libgdk-pixbuf-2.0-dev,libgtk-3-dev, WebKitGTK, Ayatana, audio, SVG, XDo, and related development packages). The Tauri/Rust and web stages passed. The gate then failed on one unrelated, reproducible pre-existing mobile test:mobile/test/features/channels/channel_detail_page_test.dart:1053ChannelDetailPage keeps follow mode off while a tall newest message stays visibleNewest live updatewidget, but one was found.The failing test also fails when rerun in isolation. No changed file is involved.
Related work
No credentials or secrets are included.