Skip to content

Conversation

xFlaviews
Copy link

@xFlaviews xFlaviews commented Aug 19, 2025

  • Automatically disable HyprCloud and fall back to HyprLLM if avalaible, otherwise to local models when license becomes invalid

Fixes Issue #1366


Summary by cubic

Automatically disables HyprCloud when the license expires and switches to a local model to keep things working: current local model first, then HyprLLM if downloaded, then any downloaded model. Fixes #1366.

  - Automatically disable HyprCloud and fall back to HyprLLM if avalaible, otherwise to local models when license becomes invalid  Fixes fastrepl#1366
Copy link

coderabbitai bot commented Aug 19, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Added React Query hooks to read and update HyprCloud enabled state, plus a side-effect that, when the license is not valid (non-pro), disables HyprCloud and the custom LLM. On successful HyprCloud disable, the enabled state is refetched. Existing license refresh logic remains.

Changes

Cohort / File(s) Summary
License component: HyprCloud state management
apps/desktop/src/components/license.tsx
Added useQuery for "hypr-cloud-enabled"; added mutations to set HyprCloud and Custom LLM enabled states; introduced useEffect to auto-disable both when license is not valid; onSuccess refetches HyprCloud state; retained existing license refresh logic.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant LicenseComponent
  participant LicenseAPI as License Query
  participant Connector as connectorCommands
  note over LicenseComponent: Mount/Update

  LicenseComponent->>LicenseAPI: getLicense (valid?)
  LicenseComponent->>Connector: getHyprcloudEnabled()
  Connector-->>LicenseComponent: hyprCloudEnabled

  alt License not valid AND HyprCloud enabled
    LicenseComponent->>Connector: setHyprcloudEnabled(false)
    Connector-->>LicenseComponent: success
    LicenseComponent->>Connector: refetch getHyprcloudEnabled()

    rect rgba(230,240,255,0.6)
    note right of LicenseComponent: Also disable Custom LLM
    LicenseComponent->>Connector: setCustomLlmEnabled(false)
    Connector-->>LicenseComponent: success
    end
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Assessment against linked issues

Objective Addressed Explanation
Deactivate HyprCloud when user is not pro (#1366)
Deactivate HyprCloud when key was de-activated (#1366) No condition or check for key deactivation present; only license validity is used.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Auto-disable Custom LLM via setCustomLlmEnabledMutation in useEffect (apps/desktop/src/components/license.tsx) Linked issue targets HyprCloud deactivation only; disabling Custom LLM is not specified.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e2b7895 and ea10100.

📒 Files selected for processing (1)
  • apps/desktop/src/components/license.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/components/license.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 1 file

React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.

}, [currentLLMModel.data, customLLMEnabled.data, setSelectedLLMModel]);

// Auto-disable HyprCloud when license expires
useEffect(() => {
Copy link

Choose a reason for hiding this comment

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

Business logic for license enforcement and model fallback is incorrectly placed within a UI component. This logic only runs when the component is mounted, meaning the HyprCloud feature will not be disabled if a user's license expires unless they happen to navigate to this specific settings page.

Prompt for AI agents
Address the following comment on apps/desktop/src/components/settings/components/ai/llm-local-view.tsx at line 51:

<comment>Business logic for license enforcement and model fallback is incorrectly placed within a UI component. This logic only runs when the component is mounted, meaning the `HyprCloud` feature will not be disabled if a user&#39;s license expires unless they happen to navigate to this specific settings page.</comment>

<file context>
@@ -47,6 +47,32 @@ export function LLMLocalView({
     }
   }, [currentLLMModel.data, customLLMEnabled.data, setSelectedLLMModel]);
 
+  // Auto-disable HyprCloud when license expires
+  useEffect(() =&gt; {
+    if (!isPro &amp;&amp; hyprCloudEnabled.data) {
+      setHyprCloudEnabledMutation.mutate(false);
</file context>

const hyprModel = llmModelsState.find(m => m.key === "HyprLLM" && m.downloaded && m.available);
if (hyprModel) {
setSelectedLLMModel("HyprLLM");
localLlmCommands.setCurrentModel("HyprLLM");
Copy link

Choose a reason for hiding this comment

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

Handle the Promise returned by setCurrentModel to avoid unhandled rejections

Prompt for AI agents
Address the following comment on apps/desktop/src/components/settings/components/ai/llm-local-view.tsx at line 63:

<comment>Handle the Promise returned by setCurrentModel to avoid unhandled rejections</comment>

<file context>
@@ -47,6 +47,32 @@ export function LLMLocalView({
     }
   }, [currentLLMModel.data, customLLMEnabled.data, setSelectedLLMModel]);
 
+  // Auto-disable HyprCloud when license expires
+  useEffect(() =&gt; {
+    if (!isPro &amp;&amp; hyprCloudEnabled.data) {
+      setHyprCloudEnabledMutation.mutate(false);
+      setCustomLLMEnabledMutation.mutate(false);
+      // Fall back to current local model if available
</file context>
Suggested change
localLlmCommands.setCurrentModel("HyprLLM");
localLlmCommands.setCurrentModel("HyprLLM").catch(console.error);

Copy link

@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: 0

🧹 Nitpick comments (2)
apps/desktop/src/components/settings/components/ai/llm-local-view.tsx (2)

247-249: Remove debugging console.log statements.

These console.log statements appear to be leftover debugging code and should be removed for production.

-                        console.log("model download clicked");
                        e.stopPropagation();
-                        console.log("model download clicked 2");
                        handleModelDownload(model.key);

74-74: Consider optimizing dependency array for better performance.

The effect has a large dependency array that could cause frequent re-runs. Consider memoizing the llmModelsState dependency or restructuring the effect to reduce unnecessary executions.

Consider extracting the model finding logic into a useMemo hook:

+  const availableModels = useMemo(() => {
+    return {
+      hyprModel: llmModelsState.find(m => m.key === "HyprLLM" && m.downloaded && m.available),
+      anyModel: llmModelsState.find(m => m.downloaded && m.available)
+    };
+  }, [llmModelsState]);

   // Auto-disable HyprCloud when license expires
   useEffect(() => {
     if (!isPro && hyprCloudEnabled.data) {
       setHyprCloudEnabledMutation.mutate(false);
       setCustomLLMEnabledMutation.mutate(false);
       // Fall back to current local model if available
       if (currentLLMModel.data) {
         setSelectedLLMModel(currentLLMModel.data);
       } else {
         // Fall back to HyprLLM (preferred default) if downloaded
-        const hyprModel = llmModelsState.find(m => m.key === "HyprLLM" && m.downloaded && m.available);
+        const hyprModel = availableModels.hyprModel;
         if (hyprModel) {
           setSelectedLLMModel("HyprLLM");
           localLlmCommands.setCurrentModel("HyprLLM");
         } else {
           // Final fallback to any available downloaded model
-          const downloadedModel = llmModelsState.find(m => m.downloaded && m.available);
+          const downloadedModel = availableModels.anyModel;
           if (downloadedModel) {
             setSelectedLLMModel(downloadedModel.key);
             localLlmCommands.setCurrentModel(downloadedModel.key as SupportedModel);
           }
         }
       }
     }
-  }, [isPro, hyprCloudEnabled.data, setHyprCloudEnabledMutation, setCustomLLMEnabledMutation, currentLLMModel.data, setSelectedLLMModel, llmModelsState]);
+  }, [isPro, hyprCloudEnabled.data, setHyprCloudEnabledMutation, setCustomLLMEnabledMutation, currentLLMModel.data, setSelectedLLMModel, availableModels]);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8eae2c8 and 9135d23.

📒 Files selected for processing (1)
  • apps/desktop/src/components/settings/components/ai/llm-local-view.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit Configuration File

**/*.{js,ts,tsx,rs}: 1. No error handling.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".

Files:

  • apps/desktop/src/components/settings/components/ai/llm-local-view.tsx
🔇 Additional comments (1)
apps/desktop/src/components/settings/components/ai/llm-local-view.tsx (1)

50-74: LGTM! License expiration handling implemented correctly.

The effect properly implements the automatic fallback behavior when the license expires. The logic correctly:

  • Disables both HyprCloud and custom LLM toggles
  • Falls back to current local model if available
  • Prefers HyprLLM as the default local model
  • Falls back to any available downloaded model as final option
  • Updates both UI state and backend model selection

Copy link

@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: 2

🧹 Nitpick comments (3)
apps/desktop/src/hooks/use-llm-settings.ts (1)

11-16: Optional: invalidate queries instead of manual refetch for better consistency

Using queryClient.invalidateQueries decouples the mutation from this particular hook instance and aligns with React Query’s recommended flow, especially if multiple components subscribe to the same key.

   const setHyprCloudEnabledMutation = useMutation({
     mutationFn: (enabled: boolean) => connectorCommands.setHyprcloudEnabled(enabled),
     onSuccess: () => {
-      hyprCloudEnabled.refetch();
+      // Prefer invalidation to let all subscribers update consistently
+      queryClient.invalidateQueries({ queryKey: ["hypr-cloud-enabled"] });
     },
   });
@@
   const setCustomLLMEnabledMutation = useMutation({
     mutationFn: (enabled: boolean) => connectorCommands.setCustomLlmEnabled(enabled),
     onSuccess: () => {
-      customLLMEnabled.refetch();
+      queryClient.invalidateQueries({ queryKey: ["custom-llm-enabled"] });
     },
   });

Additions needed outside the selected ranges:

- import { useMutation, useQuery } from "@tanstack/react-query";
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
// Inside useLlmSettings()
const queryClient = useQueryClient();

Also applies to: 23-28

apps/desktop/src/components/license.tsx (2)

19-19: Simplify isPro logic to match useLicense semantics

useLicense().getLicense already returns null when invalid. Boolean(getLicense.data) is sufficient and avoids optional chaining on .valid.

-    const isPro = !!getLicense.data?.valid;
+    const isPro = Boolean(getLicense.data);

21-25: Debounce repeated disable mutations while pending

Guarding with isPending prevents repeated .mutate(false) calls across renders if the query state hasn’t updated yet (e.g., transient failures or latency), reducing churn.

-    if (!isPro && hyprCloudEnabled.data) {
-      setHyprCloudEnabledMutation.mutate(false);
-      setCustomLLMEnabledMutation.mutate(false);
-      
-    }
+    if (
+      !isPro
+      && hyprCloudEnabled.data === true
+      && !setHyprCloudEnabledMutation.isPending
+      && !setCustomLLMEnabledMutation.isPending
+    ) {
+      setHyprCloudEnabledMutation.mutate(false);
+      setCustomLLMEnabledMutation.mutate(false);
+    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9135d23 and e2b7895.

📒 Files selected for processing (2)
  • apps/desktop/src/components/license.tsx (1 hunks)
  • apps/desktop/src/hooks/use-llm-settings.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit Configuration File

**/*.{js,ts,tsx,rs}: 1. No error handling.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".

Files:

  • apps/desktop/src/hooks/use-llm-settings.ts
  • apps/desktop/src/components/license.tsx
🧬 Code Graph Analysis (1)
apps/desktop/src/components/license.tsx (2)
apps/desktop/src/hooks/use-license.ts (1)
  • useLicense (9-105)
apps/desktop/src/hooks/use-llm-settings.ts (1)
  • useLlmSettings (5-36)
🔇 Additional comments (3)
apps/desktop/src/hooks/use-llm-settings.ts (1)

30-36: Solid hook interface

The hook’s minimal API (query results + mutation objects) is clear and composes well with React Query-driven UIs.

apps/desktop/src/components/license.tsx (2)

22-23: Confirm product intent: disabling Custom LLM when not Pro

Issue #1366 and the PR title only call out auto-disabling HyprCloud. If Custom LLM is also a Pro-only feature, this is correct; otherwise, this might unintentionally remove a valid local option.

Would you confirm whether Custom LLM should be gated behind Pro and auto-disabled on license expiry?


17-26: Missing fallback logic after HyprCloud auto-disable

I searched the codebase and didn’t find any handler that re-selects a local LLM when HyprCloud is turned off. In apps/desktop/src/components/license.tsx (lines 17–26), the effect only calls:

setHyprCloudEnabledMutation.mutate(false);
setCustomLLMEnabledMutation.mutate(false);

but never invokes the imported localLlmCommands (or any setter) to pick a fallback model.

Please implement or verify a fallback path that runs immediately after disabling HyprCloud, selecting the next available model in this order:

  • Current local model (if still present)
  • HyprLLM (if downloaded)
  • Any other downloaded local model

Locations to update:

  • apps/desktop/src/components/license.tsx (around lines 17–26): after disabling HyprCloud/Custom LLM, call into localLlmCommands or your model-selection hook.

Comment on lines 6 to 9
const hyprCloudEnabled = useQuery({
queryKey: ["hypr-cloud-enabled"],
queryFn: () => connectorCommands.getHyprcloudEnabled(),
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Type safety: explicitly type the useQuery data as boolean

Without generics, useQuery defaults to unknown for data, which can cause TS errors at call sites (e.g., hyprCloudEnabled.data used as a boolean in license.tsx). Annotate the data type as boolean for both queries.

-  const hyprCloudEnabled = useQuery({
+  const hyprCloudEnabled = useQuery<boolean>({
     queryKey: ["hypr-cloud-enabled"],
     queryFn: () => connectorCommands.getHyprcloudEnabled(),
   });
@@
-  const customLLMEnabled = useQuery({
+  const customLLMEnabled = useQuery<boolean>({
     queryKey: ["custom-llm-enabled"],
     queryFn: () => connectorCommands.getCustomLlmEnabled(),
   });

Also applies to: 18-21

🤖 Prompt for AI Agents
In apps/desktop/src/hooks/use-llm-settings.ts around lines 6-9 and 18-21, the
useQuery calls lack a generic type so their .data is typed as unknown; update
both useQuery invocations to use useQuery<boolean> (e.g., useQuery<boolean>({
queryKey: [...], queryFn: () => connectorCommands.getHyprcloudEnabled(), })) and
ensure the queryFn returns a boolean (cast the return if necessary) so callers
can safely use hyprCloudEnabled.data as a boolean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

deactivate 'hyprcloud' when key was de-activated and when user is not pro
1 participant