Skip to content

Add generic AI provider external integration type - #2744

Open
Pierre-Gilles wants to merge 7 commits into
masterfrom
claude/generic-ai-provider-integration-oebx0g
Open

Add generic AI provider external integration type#2744
Pierre-Gilles wants to merge 7 commits into
masterfrom
claude/generic-ai-provider-integration-oebx0g

Conversation

@Pierre-Gilles

@Pierre-Gilles Pierre-Gilles commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

External integrations can now declare type "ai" and serve as the AI
backend of the assistant instead of Gladys Plus. The contract is fully
provider-agnostic: the whole agentic loop (MCP tool calling, prompts,
history) stays in the core, and the integration only receives
OpenAI-compatible chat completion requests over the integration
WebSocket (external-integration.ai.chat, 120s ack) and answers with
OpenAI-compatible completions — a thin adapter to Claude, DeepSeek,
OpenAI, a local LLM or any other provider.

Server:

  • manifest type "ai" (schema + validation), no device screens
  • ai.chat proxy service capability relayed by the supervisor
  • AI_PROVIDER system variable: gateway.aiChat routes all AI traffic
    (chat loop, intent router, weekly digest) to the selected provider,
    with the Gladys Plus model field stripped and no silent fallback
  • AI works without any Gladys Plus account (message.create)
  • GET/POST /api/v1/ai_provider endpoints, selection cleared on
    uninstall of the selected provider

Front:

  • AI provider selection card on the AI integration page (admin)
  • generic external integration page branches for the ai type
  • install warning dedicated to AI providers, catalog placement
  • chat model selector hidden when an external provider is active
  • en/fr/de translations

Spec: docs/specs/external-integrations.md B.18 + C.1/C.4/C.5/C.8
updated in the same diff (spec-first rule).

Community request: https://community.gladysassistant.com/t/10421

Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01XhBhvoV4jdZEhxgadWwXV8

Summary by CodeRabbit

  • New Features
    • Added support for third-party AI providers using OpenAI-compatible chat completions.
    • Administrators can select, configure, retry, or reset the AI provider.
    • AI providers work without a Gladys Plus account.
    • Added provider availability indicators, privacy guidance, and dedicated setup flows.
  • Bug Fixes
    • Uninstalling the selected provider now restores the default AI service.
    • Improved installation routing and navigation for AI providers.
    • AI requests consistently use the selected provider and its configuration.
    • Added clearer handling for unavailable, stopped, or misconfigured providers.

External integrations can now declare type "ai" and serve as the AI
backend of the assistant instead of Gladys Plus. The contract is fully
provider-agnostic: the whole agentic loop (MCP tool calling, prompts,
history) stays in the core, and the integration only receives
OpenAI-compatible chat completion requests over the integration
WebSocket (external-integration.ai.chat, 120s ack) and answers with
OpenAI-compatible completions — a thin adapter to Claude, DeepSeek,
OpenAI, a local LLM or any other provider.

Server:
- manifest type "ai" (schema + validation), no device screens
- ai.chat proxy service capability relayed by the supervisor
- AI_PROVIDER system variable: gateway.aiChat routes all AI traffic
  (chat loop, intent router, weekly digest) to the selected provider,
  with the Gladys Plus model field stripped and no silent fallback
- AI works without any Gladys Plus account (message.create)
- GET/POST /api/v1/ai_provider endpoints, selection cleared on
  uninstall of the selected provider

Front:
- AI provider selection card on the AI integration page (admin)
- generic external integration page branches for the ai type
- install warning dedicated to AI providers, catalog placement
- chat model selector hidden when an external provider is active
- en/fr/de translations

Spec: docs/specs/external-integrations.md B.18 + C.1/C.4/C.5/C.8
updated in the same diff (spec-first rule).

Community request: https://community.gladysassistant.com/t/10421

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhBhvoV4jdZEhxgadWwXV8
@github-actions github-actions Bot added area:server Node.js server code area:front Preact front-end area:ai AI agent, routing, prompts type:feature New user-facing feature or improvement labels Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change adds ai external integrations with OpenAI-compatible chat over WebSocket. Administrators can select providers through new APIs and UI settings. AI requests route to the selected provider, with lifecycle cleanup, validation, translations, and tests.

Changes

External AI provider

Layer / File(s) Summary
AI integration contract and proxy runtime
docs/specs/external-integrations.md, server/lib/external-integration/*, server/utils/constants.js, server/test/lib/external-integration/*
AI manifests support type: "ai", the ai.chat capability, OpenAI-compatible payloads, and a 120-second timeout.
Provider selection and lifecycle
server/api/controllers/externalIntegration.controller.js, server/api/routes.js, server/lib/external-integration/*, server/test/controllers/externalIntegration/*
New APIs list, select, and reset providers. Uninstalling the selected provider clears AI_PROVIDER.
Gateway and message routing
server/lib/gateway/gateway.aiChat.js, server/lib/message/message.create.js, server/test/lib/gateway/*, server/test/lib/message/*
Configured providers receive AI requests without the Gladys Plus model or fallback. AI messages work without Gladys Plus configuration.
Provider configuration and integration UX
front/src/routes/integration/*, front/src/routes/chat/AiModelSelector.jsx, front/src/config/i18n/*
Administrators can select providers. AI integrations route to configuration screens, hide device screens, suppress the model selector, and display localized warnings and error states.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AiProviderSettings
  participant API
  participant ExternalIntegration
  participant AIProvider
  User->>AiProviderSettings: Select AI provider
  AiProviderSettings->>API: POST /api/v1/ai_provider
  API->>ExternalIntegration: setAiProvider(selector)
  ExternalIntegration-->>API: Save AI_PROVIDER
  User->>API: Send AI chat request
  API->>AIProvider: ai.chat(request)
  AIProvider-->>API: OpenAI-compatible completion
Loading

Possibly related PRs

Suggested labels: enhancement, Service Development

Suggested reviewers: atrovato

Poem

A rabbit selects a provider bright,
Chat hops through WebSocket light.
The model hides when providers speak,
Admin settings make the choice unique.
When one is removed, its setting clears.

🚥 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 clearly summarizes the main change: adding a generic AI provider external integration type.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/generic-ai-provider-integration-oebx0g

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Review of Add generic AI provider external integration type (#2744).

Solid, spec-first design: provider-agnostic OpenAI-compatible relay, explicit admin selection, no silent Gladys Plus fallback, install trust warning, and AI without Plus via message.create. Tests cover the new relay, routing, selection, uninstall cleanup, and API RBAC. No brand-specific device categories in constants.js.

Not approving. This is the highest-trust external-integration class (conversations + tool results + device/scene authority). Labeled risk:high and needs:human-review; requesting @atrovato (author is already Pierre-Gilles).

Must resolve before merge

  1. Spec section number collision with #2738 — that open PR already claims B.18 for weather. This branch should use B.19 (or renumber weather), including the phase-2 table and C.1/C.4/C.5/C.8 cross-refs.
  2. Living-spec banner still outdated — the document header still says “B.15–B.17 are phase 2 designs not yet implemented” while this PR ships a new B.18. Update the banner in the same diff (weather’s branch already rewrote it).
  3. Incomplete ai front redirectsExternalIntegrationPage correctly drops device tabs for ai, and install/catalog URLs go to /config, but device-page/index.js and discover-page/index.js still only redirect communication. Direct /device or /discover URLs for an AI provider can still hit empty device screens.

Also note

  • Vendored manifest.schema.json accepts ai; the store indexer (GladysAssistant/integration-store) and SDK (onAiChat) need matching companion updates or catalog installs/onAiChat will lag the core.
  • Completion validation only rejects non-objects; empty {} still reaches extractAssistantMessage and fails softer as openai.request.fail. Tightening is optional.

Inline comments below.

Open in Web View Automation 

Sent by Cursor Automation: Automatic PR review

Comment thread docs/specs/external-integrations.md Outdated
Comment thread docs/specs/external-integrations.md Outdated
Comment thread server/lib/external-integration/externalIntegration.aiChat.js Outdated
Comment thread server/lib/external-integration/manifest.schema.json
Comment thread server/lib/gateway/gateway.aiChat.js
@cursor
cursor Bot requested a review from atrovato August 2, 2026 06:24
@Pierre-Gilles Pierre-Gilles added needs:human-review Automated review is not confident, maintainer must take a look risk:high Touches DB migrations, auth, billing or user data. Careful human review required labels Aug 2, 2026 — with Cursor
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🐳 A Docker image has been built for this branch and pushed to the GitHub Container Registry.

You can test this pull request (AMD64 only) by pulling the image below:

ghcr.io/gladysassistant/gladys-preview:claude-generic-ai-provider-integration-oebx0g

For example, run it with:

sudo docker run -d \
  --log-driver json-file \
  --log-opt max-size=10m \
  --cgroupns=host \
  --restart=always \
  --privileged \
  --network=host \
  --name gladys-claude-generic-ai-provider-integration-oebx0g \
  -e NODE_ENV=production \
  -e SERVER_PORT=80 \
  -e TZ=Europe/Paris \
  -e SQLITE_FILE_PATH=/var/lib/gladysassistant/gladys-production.db \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/gladysassistant:/var/lib/gladysassistant \
  -v /dev:/dev \
  -v /run/udev:/run/udev:ro \
  ghcr.io/gladysassistant/gladys-preview:claude-generic-ai-provider-integration-oebx0g

This comment and the image are automatically updated on every new commit pushed to this pull request.

Need an ARM64 image (Raspberry Pi, Apple Silicon, …)? Comment /build-arm64 on this pull request.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@server/lib/external-integration/externalIntegration.aiChat.js`:
- Around line 26-29: Strengthen the completion validation in the result-handling
code by requiring a non-empty choices array and an object at choices[0].message
before returning the response. Preserve throwing
ExternalIntegrationUnavailableError with
EXTERNAL_INTEGRATION_INVALID_AI_RESPONSE for all malformed structures, and add
unit-test cases for an empty object, an empty choices array, and a missing or
invalid first message.

In `@server/lib/external-integration/externalIntegration.uninstall.js`:
- Around line 55-60: Serialize setAiProvider() with uninstall() for the same
integration so provider selection cannot be written after uninstall has checked
or removed the service. Update the synchronization or availability validation
around these methods, preserving normal provider selection while ensuring
AI_PROVIDER is never saved with the uninstalling service.selector.

In `@server/test/lib/message/message.create.test.js`:
- Around line 76-105: The test around MessageHandler.create must also verify
that the Gladys Plus-required fallback event is not emitted when an external AI
provider is configured. Extend the existing event.emit assertions in the test to
assert no openai.plus-required reply is sent, while preserving the current
message.new-for-open-ai assertion and covering the new branch.
🪄 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 Plus

Run ID: c7907e60-adea-4387-8623-4c289a9f7fa4

📥 Commits

Reviewing files that changed from the base of the PR and between c4e452a and 36eaa96.

📒 Files selected for processing (30)
  • docs/specs/external-integrations.md
  • front/src/config/i18n/de.json
  • front/src/config/i18n/en.json
  • front/src/config/i18n/fr.json
  • front/src/routes/chat/AiModelSelector.jsx
  • front/src/routes/integration/all/external-integration/ExternalIntegrationPage.jsx
  • front/src/routes/integration/all/external-integration/install-from-github/InstallFromGithubCard.jsx
  • front/src/routes/integration/all/external-integration/install-page/index.js
  • front/src/routes/integration/all/openai/AiProviderSettings.jsx
  • front/src/routes/integration/all/openai/index.js
  • front/src/routes/integration/index.js
  • server/api/controllers/externalIntegration.controller.js
  • server/api/routes.js
  • server/lib/external-integration/constants.js
  • server/lib/external-integration/externalIntegration.aiChat.js
  • server/lib/external-integration/externalIntegration.getAiProviders.js
  • server/lib/external-integration/externalIntegration.registerProxyService.js
  • server/lib/external-integration/externalIntegration.setAiProvider.js
  • server/lib/external-integration/externalIntegration.uninstall.js
  • server/lib/external-integration/externalIntegration.validateManifest.js
  • server/lib/external-integration/index.js
  • server/lib/external-integration/manifest.schema.json
  • server/lib/gateway/gateway.aiChat.js
  • server/lib/message/message.create.js
  • server/test/controllers/externalIntegration/aiProvider.controller.test.js
  • server/test/lib/external-integration/externalIntegration.aiProvider.test.js
  • server/test/lib/external-integration/testUtils.test.js
  • server/test/lib/gateway/gateway.aiChat.unit.test.js
  • server/test/lib/message/message.create.test.js
  • server/utils/constants.js

Comment thread server/lib/external-integration/externalIntegration.aiChat.js
Comment thread server/lib/external-integration/externalIntegration.uninstall.js
Comment thread server/test/lib/message/message.create.test.js
@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.16%. Comparing base (e427b54) to head (6d43ed9).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2744   +/-   ##
=======================================
  Coverage   99.15%   99.16%           
=======================================
  Files        1190     1193    +3     
  Lines       25057    25120   +63     
=======================================
+ Hits        24846    24909   +63     
  Misses        211      211           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Renumber spec section B.18 to B.19 (B.18 is taken by the weather type
  in the parallel PR #2738) and update the living-spec banner to reflect
  the implemented state of B.15-B.17 and B.19
- Redirect direct URL access to the device/discover screens of an AI
  integration to its configuration screen (same rule as communication)
- Validate the ai.chat completion at the contract boundary: data must
  carry an object choices[0].message, so adapter bugs surface as an
  explicit invalid-response error instead of a silent empty turn
- Guard setAiProvider against a concurrent uninstall of the selected
  provider (re-check after write, roll back the variable)
- Assert the plus-required reply is not sent when an external AI
  provider serves the assistant (message.create test)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhBhvoV4jdZEhxgadWwXV8
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 2, 2026

Copy link
Copy Markdown

Deploying gladys-plus with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6d43ed9
Status: ✅  Deploy successful!
Preview URL: https://e7a18a81.gladys-plus.pages.dev
Branch Preview URL: https://claude-generic-ai-provider-i.gladys-plus.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@server/lib/external-integration/externalIntegration.setAiProvider.js`:
- Around line 27-37: Make the AI-provider selection and
externalIntegration.uninstall cleanup atomic rather than relying on the
post-write stillInstalled check in setAiProvider. Serialize both operations or
use an atomic conditional update/delete so uninstall cannot leave or recreate
AI_PROVIDER with the removed service_id; add a concurrency test covering the
interleaving.
🪄 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 Plus

Run ID: b64b00b9-b31b-45d3-9a69-29406ef2950d

📥 Commits

Reviewing files that changed from the base of the PR and between 36eaa96 and f6898f6.

📒 Files selected for processing (9)
  • docs/specs/external-integrations.md
  • front/src/routes/integration/all/external-integration/device-page/index.js
  • front/src/routes/integration/all/external-integration/discover-page/index.js
  • server/lib/external-integration/externalIntegration.aiChat.js
  • server/lib/external-integration/externalIntegration.setAiProvider.js
  • server/lib/gateway/gateway.aiChat.js
  • server/test/lib/external-integration/externalIntegration.aiProvider.test.js
  • server/test/lib/external-integration/testUtils.test.js
  • server/test/lib/message/message.create.test.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • server/lib/external-integration/externalIntegration.aiChat.js
  • server/test/lib/message/message.create.test.js
  • server/test/lib/external-integration/externalIntegration.aiProvider.test.js
  • server/test/lib/external-integration/testUtils.test.js
  • server/lib/gateway/gateway.aiChat.js

Pierre-Gilles pushed a commit that referenced this pull request Aug 2, 2026
…ndbox fix

Review fixes (cursor bot, blocking):
- renumber the TTS provider section from B.18 to B.20 everywhere (spec,
  schema comments, code comments): B.18 is owned by the weather type
  (#2738) and B.19 by the AI provider type (#2744)
- clear TTS_ACTIVE_PROVIDER when uninstalling the selected provider
  (the AI_PROVIDER pattern of #2744): no dangling selector failing every
  announcement; documented in B.20 and C.5, covered by tests
- living-spec truthfulness: the banner now states B.15-B.17 are
  implemented and reserves B.18/B.19 for the parallel PRs; the
  "current state" block of B.20 is rewritten as pre-refactor state

Review fixes (cursor bot + CodeRabbit, non-blocking):
- GET /api/v1/tts/provider now returns a display name per provider (the
  integration manifest name) so the System settings select matches the
  Integrations UI instead of showing raw selectors
- getLocalApiBaseUrl prefers RFC1918 IPv4 addresses on multi-homed hosts
  (VPN tunnels or virtual bridges enumerating first)
- own-key lookup on the TTS content-type allow-list (inherited
  Object.prototype keys such as "constructor" are rejected)
- defense-in-depth validation in tts.getSpeechUrl: buffer type, size
  bound and curated content type/extension are re-checked before any
  byte is cached or served, whatever service implements the provider
  interface
- TTS provider settings card commits the selection only after the POST
  succeeds (select disabled while saving, reload on error)
- device/discover pages resolve the integration type before firing
  device requests, so config-only integrations redirect cleanly
- the tts catalog category is hidden from non-admin users
- mirrored unit tests for tts.getProviderConfiguration and
  tts.purgeExpiredAudios

CI fix (Server test failure, 300+ cascading failures):
- tts.getLocalApiBaseUrl.test.js called sinon.restore() on the DEFAULT
  sandbox in afterEach, untracking the fakes other test files create at
  load time: their sinon.reset() cleanup stopped clearing call history
  and every suite running after the file leaked call counts ("called
  twice" assertions, fake-timer timeouts). The stub now lives in a
  dedicated sinon sandbox (the documented test/models/index.test.js
  pattern). Full suite verified locally: only the environment-specific
  failures of this sandbox remain (outbound proxy vs nock, missing
  sqlite3 binary).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ihJhVk52zW6qUsxBHeqx2
@Pierre-Gilles Pierre-Gilles added the needs:cursor-review Automated review by Cursor is needed label Aug 2, 2026
@Pierre-Gilles Pierre-Gilles removed the needs:cursor-review Automated review by Cursor is needed label Aug 2, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Re-review of f6898f6 (follow-up to the first Cursor pass).

Verdict

Architecture matches B.19 well: provider-agnostic OpenAI-compatible relay, agentic loop stays in core, explicit admin selection, no silent Gladys Plus fallback, uninstall clears AI_PROVIDER, AI stays admin-managed, and non-admin list/detail RBAC still filters to communication only. Previous blockers (B.18→B.19 renumber, living-spec banner, device/discover redirects, completion choices[0].message validation, concurrent-uninstall re-check) are addressed. CI is green including Codecov patch.

Not approving: this is the highest-trust external-integration class (conversations + tool results + home/scene authority). Keeping risk:high + needs:human-review for a maintainer product pass. @atrovato is already requested.

Remaining notes

  1. Trust warning coverage — the store install page warns for type: "ai", but until the indexer accepts ai, GitHub/dev install is the practical path and shows no equivalent warning. Selection in AiProviderSettings is immediate with no restated trust copy either (see inline).
  2. AI page upsell — with Plus disconnected, the Gladys Plus upsell still dominates the AI page even when an external provider is selected and chat already works via message.create (see inline).
  3. Soft residuals (ok for human review, not blocking): instance-wide single provider for chat/intent/digest; companion indexer/SDK onAiChat still needed for catalog listing (graceful until then per Pierre’s note).

No new brand-tied device categories/types in server/utils/constants.js — only AI_PROVIDER + external-integration.ai.chat.

Open in Web View Automation 

Sent by Cursor Automation: Automatic PR review

Comment thread front/src/routes/integration/all/openai/AiProviderSettings.jsx
Comment thread front/src/routes/integration/all/openai/index.js Outdated
…us upsell

Cursor re-review follow-ups:
- The trust warning shown on the store install screen never appears on
  the GitHub/dev install path, and selection is the moment conversations
  actually start flowing to the provider: the AI provider settings card
  now restates the warning whenever an external provider is selected
- With an external provider active, the assistant already works without
  Gladys Plus: the Plus upsell and the Plus rate-limit note are hidden
  on the AI page in that case

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhBhvoV4jdZEhxgadWwXV8

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
front/src/routes/integration/all/openai/index.js (1)

141-141: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Update externalProviderActive after provider selection.

getAiProvider() runs only in componentDidMount(). AiProviderSettings.updateProvider() updates the child state after POST /api/v1/ai_provider, but OpenAIGateway receives no update. Selecting an external provider therefore does not hide the upsell or rate-limit message on the current page. Resetting to Gladys Plus can leave those messages hidden until reload.

Pass an onProviderChange callback to AiProviderSettings, or lift the selector state into OpenAIGateway, and update it after the save succeeds.

The supplied front/src/routes/integration/all/openai/AiProviderSettings.jsx:13-52 snippet shows that the child updates only its own state.

🤖 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 `@front/src/routes/integration/all/openai/index.js` at line 141, Update
OpenAIGateway’s provider-selection flow so externalProviderActive is refreshed
after AiProviderSettings.updateProvider() successfully saves a provider. Pass an
onProviderChange callback to AiProviderSettings or lift the state into
OpenAIGateway, ensuring both external-provider selection and resetting to Gladys
Plus immediately update the current page’s upsell and rate-limit messaging.
🤖 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 `@front/src/routes/integration/all/openai/index.js`:
- Around line 30-39: Update getAiProvider and the externalProviderActive
initialization to use null as the unknown state, preserving false only when the
provider response confirms no external provider. In the affected Gladys Plus
upsell and rate-limit render conditions, require externalProviderActive ===
false so messages remain hidden while the request is pending or fails, while
retaining the external-provider-active behavior.

---

Outside diff comments:
In `@front/src/routes/integration/all/openai/index.js`:
- Line 141: Update OpenAIGateway’s provider-selection flow so
externalProviderActive is refreshed after AiProviderSettings.updateProvider()
successfully saves a provider. Pass an onProviderChange callback to
AiProviderSettings or lift the state into OpenAIGateway, ensuring both
external-provider selection and resetting to Gladys Plus immediately update the
current page’s upsell and rate-limit messaging.
🪄 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 Plus

Run ID: eb8c09ac-7916-41e7-8be4-33710f1a231c

📥 Commits

Reviewing files that changed from the base of the PR and between f6898f6 and ddd7a80.

📒 Files selected for processing (9)
  • docs/specs/external-integrations.md
  • front/src/config/i18n/de.json
  • front/src/config/i18n/en.json
  • front/src/config/i18n/fr.json
  • front/src/routes/integration/all/openai/AiProviderSettings.jsx
  • front/src/routes/integration/all/openai/index.js
  • server/lib/external-integration/constants.js
  • server/lib/external-integration/index.js
  • server/test/lib/external-integration/testUtils.test.js
🚧 Files skipped from review as they are similar to previous changes (8)
  • server/lib/external-integration/index.js
  • server/test/lib/external-integration/testUtils.test.js
  • server/lib/external-integration/constants.js
  • front/src/config/i18n/en.json
  • front/src/config/i18n/fr.json
  • front/src/config/i18n/de.json
  • front/src/routes/integration/all/openai/AiProviderSettings.jsx
  • docs/specs/external-integrations.md

Comment thread front/src/routes/integration/all/openai/index.js
CodeRabbit follow-ups on the AI page:
- externalProviderActive starts as null (unknown): the Plus upsell and
  rate-limit messages only render once the absence of an external
  provider is confirmed, instead of flashing while the request is
  pending or lying after a failed request
- AiProviderSettings notifies the page through onProviderChange, so
  selecting or resetting the provider updates the messaging without a
  reload

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhBhvoV4jdZEhxgadWwXV8
@Pierre-Gilles Pierre-Gilles added needs:cursor-review Automated review by Cursor is needed and removed needs:cursor-review Automated review by Cursor is needed labels Aug 2, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Re-review of bdbd983 (follow-up to the Cursor pass on f6898f6).

Verdict

Architecture still matches B.19 well: provider-agnostic OpenAI-compatible relay, agentic loop stays in core, explicit admin selection, no silent Gladys Plus fallback, uninstall clears AI_PROVIDER, AI stays admin-managed, non-admin list/detail RBAC still filters to communication only. Previous soft notes are fixed in ddd7a802 / bdbd983:

  • trust warning restated under the provider select
  • Plus upsell / rate-limit copy gated on confirmed absence of an external provider (null while loading/failed)
  • parent page kept in sync via onProviderChange

CI is green (including Codecov patch). No brand-tied device categories/types — only AI_PROVIDER + external-integration.ai.chat.

Not approving. This remains the highest-trust external-integration class (conversations + tool results + home/scene authority via the tool-calling loop). Keeping risk:high + needs:human-review for a maintainer product pass. @atrovato is already requested.

Soft residual for human review

  1. Plus connected + external provider — when Gladys Plus is configured and an external provider is selected, the page still shows the Plus “chat enabled” success line, AiQuotaDisplay, and Plus-only debug download. Quota especially is misleading once chat traffic no longer hits Plus (see inline).
  2. Companion store indexer / SDK onAiChat still needed for catalog listing (graceful until then; GitHub/dev install remains the practical path).
  3. Living-spec body: B.15–B.17 section titles still say “(design, phase 2)” while the banner correctly treats them as shipped — cosmetic inconsistency across parallel provider PRs, not a merge blocker for this change.
Open in Web View Automation 

Sent by Cursor Automation: Automatic PR review

Comment thread front/src/routes/integration/all/openai/index.js Outdated
…tive

With Gladys Plus connected and an external provider selected, the AI
traffic no longer hits Plus: the Plus quota display and the 'chat
enabled by default' banner were misleading in that state. Both are now
gated on the confirmed absence of an external provider, like the upsell
and rate-limit messages. The weekly digest and debug context download
stay: their LLM calls follow the selected provider through
gateway.aiChat.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhBhvoV4jdZEhxgadWwXV8
…rovider-integration-oebx0g

# Conflicts:
#	front/src/routes/integration/all/external-integration/discover-page/index.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (4)
front/src/routes/integration/all/external-integration/discover-page/index.js (4)

133-149: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Keep the post-create refresh on the originating page.

Capture pageGeneration before the POST. If the selector changes while the POST is pending, return before calling getDiscoveredDevices(). Otherwise, that method uses the new selector and updates the wrong page. After unmount, it can also call setState before its own generation guard runs.

Proposed lifecycle guard
 createDevice = async externalId => {
+  const generation = this.pageGeneration;
   const discoveredDevice = this.state.discoveredDevices.find(device => device.external_id === externalId);
...
   await this.props.httpClient.post('/api/v1/device', device);
+  if (generation !== this.pageGeneration) {
+    return;
+  }
   await this.getDiscoveredDevices();
 };
🤖 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 `@front/src/routes/integration/all/external-integration/discover-page/index.js`
around lines 133 - 149, Update createDevice to capture the current
pageGeneration before posting, then verify it is unchanged after the POST and
return if the selector changed or the component is no longer current. Only call
getDiscoveredDevices when the originating page generation remains valid,
preventing refreshes against a different page or after unmount.

87-89: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not report success when the scan completion event is missing.

When the 60-second timer invokes finishScan, the method sets RequestStatus.Success but does not call getDiscoveredDevices(). The page can show success with the old device list. A later POST failure is also ignored after scanToken changes. Use an explicit timeout/error state, or perform an authoritative refresh before reporting success.

Also applies to: 110-120

🤖 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 `@front/src/routes/integration/all/external-integration/discover-page/index.js`
around lines 87 - 89, Update the scan timeout flow around finishScan so a
missing scan completion event cannot set RequestStatus.Success with stale
devices. On timeout, either set an explicit error/timeout status or fetch the
authoritative device list with getDiscoveredDevices() before reporting success;
ensure late POST failures after scanToken changes are not silently ignored.

20-22: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Cancel the debounced search when the page loses focus.

loadIntegrationPage() and componentWillUnmount() bump pageGeneration and clear the scan timer, but a pending debounce can still run and call setState with the old selector. Use debounce(...).clear() at those same points. If searching is page-scoped, reset deviceSearch when the debounced call is cleared.

🤖 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 `@front/src/routes/integration/all/external-integration/discover-page/index.js`
around lines 20 - 22, Update loadIntegrationPage() and componentWillUnmount() to
clear the pending debouncedSearchDevices invocation alongside the existing
pageGeneration and scan-timer cleanup, using its debounce clear API. When
clearing the page-scoped search, also reset deviceSearch so stale selector
results cannot update state after focus loss or unmount.

58-74: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject out-of-order device refresh responses.

pageGeneration changes only when the selector changes. The websocket handler and createDevice can start several requests for one generation. If an older request resolves after a newer request, Lines 67 and 73 overwrite the current list or status with stale data. Track a request sequence token and check it in both the success and error paths.

Proposed request-order guard
   this.scanToken = 0;
+  this.discoveredDevicesRequestToken = 0;

   getDiscoveredDevices = async () => {
     const generation = this.pageGeneration;
+    const requestToken = ++this.discoveredDevicesRequestToken;
     this.setState({ getDiscoveredDevicesStatus: RequestStatus.Getting });
...
-      if (generation !== this.pageGeneration) {
+      if (
+        generation !== this.pageGeneration ||
+        requestToken !== this.discoveredDevicesRequestToken
+      ) {
         return;
       }
...
-      if (generation !== this.pageGeneration) {
+      if (
+        generation !== this.pageGeneration ||
+        requestToken !== this.discoveredDevicesRequestToken
+      ) {
         return;
       }
🤖 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 `@front/src/routes/integration/all/external-integration/discover-page/index.js`
around lines 58 - 74, Update the discovered-device refresh flow around the
request in the component method containing pageGeneration to track a
monotonically increasing request sequence token for every request, including
websocket and createDevice-triggered refreshes within the same generation.
Capture the token before awaiting httpClient.get, and require both the
generation and token to still match before applying discoveredDevices or
GetDiscoveredDevicesStatus in the success and error paths.
🤖 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.

Outside diff comments:
In
`@front/src/routes/integration/all/external-integration/discover-page/index.js`:
- Around line 133-149: Update createDevice to capture the current pageGeneration
before posting, then verify it is unchanged after the POST and return if the
selector changed or the component is no longer current. Only call
getDiscoveredDevices when the originating page generation remains valid,
preventing refreshes against a different page or after unmount.
- Around line 87-89: Update the scan timeout flow around finishScan so a missing
scan completion event cannot set RequestStatus.Success with stale devices. On
timeout, either set an explicit error/timeout status or fetch the authoritative
device list with getDiscoveredDevices() before reporting success; ensure late
POST failures after scanToken changes are not silently ignored.
- Around line 20-22: Update loadIntegrationPage() and componentWillUnmount() to
clear the pending debouncedSearchDevices invocation alongside the existing
pageGeneration and scan-timer cleanup, using its debounce clear API. When
clearing the page-scoped search, also reset deviceSearch so stale selector
results cannot update state after focus loss or unmount.
- Around line 58-74: Update the discovered-device refresh flow around the
request in the component method containing pageGeneration to track a
monotonically increasing request sequence token for every request, including
websocket and createDevice-triggered refreshes within the same generation.
Capture the token before awaiting httpClient.get, and require both the
generation and token to still match before applying discoveredDevices or
GetDiscoveredDevicesStatus in the success and error paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 779faed4-9e1c-4afc-8dbf-49f61342e7c5

📥 Commits

Reviewing files that changed from the base of the PR and between bdbd983 and 6d43ed9.

📒 Files selected for processing (8)
  • docs/specs/external-integrations.md
  • front/src/config/i18n/de.json
  • front/src/config/i18n/en.json
  • front/src/config/i18n/fr.json
  • front/src/routes/integration/all/external-integration/discover-page/index.js
  • front/src/routes/integration/all/openai/index.js
  • server/lib/external-integration/constants.js
  • server/utils/constants.js
🚧 Files skipped from review as they are similar to previous changes (6)
  • server/lib/external-integration/constants.js
  • front/src/config/i18n/de.json
  • server/utils/constants.js
  • docs/specs/external-integrations.md
  • front/src/routes/integration/all/openai/index.js
  • front/src/config/i18n/fr.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ai AI agent, routing, prompts area:front Preact front-end area:server Node.js server code needs:human-review Automated review is not confident, maintainer must take a look risk:high Touches DB migrations, auth, billing or user data. Careful human review required type:feature New user-facing feature or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants