Skip to content

Conversation

@HuberTRoy
Copy link
Member

@HuberTRoy HuberTRoy commented Oct 30, 2025

Description

  • Remove sv in project detail page.
  • Add graphql agent in project detail page.

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

Summary by CodeRabbit

  • New Features

    • Launches GraphQL Agent chat for interactive project queries with streaming responses and per-message timestamps
    • Adds expandable "Think blocks" showing intermediate reasoning and suggested questions
    • Enhanced indexer selection controls within project view and replaced Service Agreements tab with GraphQL Agents
  • Chores

    • Added UI styles and support utilities to enable the new chat experience

@HuberTRoy HuberTRoy requested a review from Copilot October 30, 2025 02:54
@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 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.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is largely incomplete compared to the provided template. The Description section is present but extremely brief and vague (e.g., "Remove sv in project detail page" is cryptic and unclear—likely referring to Service Agreements), contains no ticket reference, lacks motivation or context, and provides no information about dependencies. Critically, the Test cases section is entirely missing, and the Type of change section still contains template text that should have been removed. The description provides insufficient detail for reviewers to understand the scope, rationale, or validation of the changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The pull request title "Feat/agent" is extremely abbreviated and uses non-descriptive formatting that resembles a branch name rather than a meaningful PR title. While it does technically reference "agent," which relates to the GraphQL Agent feature addition in the changeset, it fails to convey any meaningful information about what was changed. The title does not clearly summarize that both a removal (Service Agreements) and addition (GraphQL Agent) occurred, nor does it provide sufficient clarity for a teammate scanning the commit history to understand the scope of the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/agent

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f89286 and 9b3b5b5.

📒 Files selected for processing (1)
  • src/components/IndexerDetails/IndexerDetails.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/IndexerDetails/IndexerDetails.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

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

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds GraphQL Agent functionality to enable users to interact with indexed data through a conversational chat interface. The implementation includes agent registration, streaming chat capabilities, and an intuitive UI for querying SubQuery projects.

Key Changes:

  • Added GraphQL Agent API services and hooks for project registration and chat interactions
  • Replaced "Service Agreements" tab with "GraphQL Agents" tab in project details
  • Integrated agent chat interface with operator selection and token-based authentication

Reviewed Changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 23 comments.

Show a summary per file
File Description
package.json, yarn.lock Added lucide-react icon library dependency
src/types/index.ts Defined TypeScript interfaces for projects, chat, and API requests/responses
src/services/agent-api.ts Created API service layer for agent operations (register, chat, streaming)
src/hooks/useProjects.ts React Query hooks for project CRUD operations
src/hooks/useChat.ts Chat hook with streaming message support and data limit controls
src/components/GraphqlAgent/* Chat interface component with message bubbles, think blocks, and controls
src/pages/explorer/Project/* Integrated GraphQL Agent tab into project view with operator selection
src/lib/utils.ts Utility functions for formatting, validation, and clipboard operations
src/lib/gtm.ts Google Tag Manager event tracking for analytics
src/hooks/useSpaceId.tsx Updated RPC endpoint configuration to use environment variables
.env, .env.staging Added VITE_GRAPHQL_AGENT and removed hard-coded RPC URLs
src/components/IndexerDetails/* Modified table column widths and removed hard-coded columns array

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
await registerProject.mutateAsync({
cid: deploymentId || '',
endpoint: queryUrl || '',
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The endpoint is using the stale queryUrl state instead of the newly updated url parameter. On line 167, setQueryUrl(url) is called, but state updates are asynchronous in React, so line 183 will use the old value. Use the url parameter directly instead of queryUrl || ''.

Suggested change
endpoint: queryUrl || '',
endpoint: url,

Copilot uses AI. Check for mistakes.
'https://ethereum.publicnode.com',
'https://eth.merkle.io',
'https://eth.drpc.org',
import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC,
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

The environment variable VITE_SUBQUERY_OFFICIAL_ETH_RPC is referenced but not defined in any of the environment files (.env, .env.staging, .env.testnet). This will cause runtime errors when this RPC endpoint is needed.

Copilot uses AI. Check for mistakes.
loading?: boolean;
}

// 新增:可折叠的think块组件
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Comment contains Chinese characters. Should be translated to English for consistency with the codebase.

Suggested change
// 新增:可折叠的think块组件
// Added: Collapsible think block component

Copilot uses AI. Check for mistakes.
const lastContentUpdateRef = useRef(Date.now());
const autoScrollTimeoutRef = useRef<ReturnType<typeof setTimeout>>();

// 检测用户是否在底部附近(阈值为30px)
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Comment contains Chinese characters. Should be translated to English for consistency with the codebase.

Suggested change
// 检测用户是否在底部附近(阈值为30px)
// Check if the user is near the bottom (threshold: 30px)

Copilot uses AI. Check for mistakes.
return scrollHeight - scrollTop - clientHeight < 30;
};

// 滚动到底部的函数
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Comment contains Chinese characters. Should be translated to English for consistency with the codebase.

Suggested change
// 滚动到底部的函数
// Function to scroll to the bottom

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,63 @@
import { type ClassValue, clsx } from 'clsx';
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Unused import clsx.

Suggested change
import { type ClassValue, clsx } from 'clsx';

Copilot uses AI. Check for mistakes.
// SPDX-License-Identifier: Apache-2.0

import * as React from 'react';
import { useMemo } from 'react';
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Unused import useMemo.

Suggested change
import { useMemo } from 'react';

Copilot uses AI. Check for mistakes.
import { IndexerName } from '@components/IndexerDetails/IndexerName';
import { useAccount } from '@containers/Web3';
import { useIndexerMetadata } from '@hooks';
import { useConsumerHostServices } from '@hooks/useConsumerHostServices';
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Unused import useConsumerHostServices.

Suggested change
import { useConsumerHostServices } from '@hooks/useConsumerHostServices';

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +20
useAsyncMemo,
useGetDeploymentIndexersLazyQuery,
useGetIndexerDeploymentLazyQuery,
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

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

Unused imports useAsyncMemo, useGetIndexerDeploymentLazyQuery.

Suggested change
useAsyncMemo,
useGetDeploymentIndexersLazyQuery,
useGetIndexerDeploymentLazyQuery,
useGetDeploymentIndexersLazyQuery,

Copilot uses AI. Check for mistakes.
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: 9

🧹 Nitpick comments (2)
src/components/IndexerDetails/IndexerDetails.module.less (1)

182-193: Use CSS variables for color values to maintain design consistency.

The new .agentModal class uses hard-coded color values (#000) instead of CSS variables, which is inconsistent with the design system used throughout the rest of this file (e.g., var(--sq-blue600), var(--gray700)). This makes the code harder to maintain and theme.

Additionally, the redundant hover state (line 187-189) can be simplified, as it sets the same color as the normal state.

Consider refactoring to use CSS variables and simplifying the hover state:

 .agentModal {
   :global {
     .ant-modal-content {
       .ant-modal-close-x{
-        color: #000;
-        &:hover {
-          color: #000;
-        }
+        color: var(--gray900);
       }
     }
   }
 }

(Adjust var(--gray900) or the appropriate variable based on your design system's naming conventions for black.)

Please verify:

  1. Whether a CSS variable exists in the design system for the black color value
  2. Whether the color provides sufficient contrast against the modal background for accessibility
src/hooks/useProjects.ts (1)

6-12: Consider whether 30-second refetch interval is optimal.

The projects list refetches every 30 seconds. Depending on how frequently projects are added/updated, this might be:

  • Too aggressive if projects rarely change (could increase to 60s+)
  • Too conservative if real-time updates are important (could decrease or use websockets)

Current implementation is reasonable as a starting point.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6c6f16 and 4f89286.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (17)
  • .env (1 hunks)
  • .env.staging (1 hunks)
  • package.json (1 hunks)
  • src/components/GraphqlAgent/graphqlAgent.module.less (1 hunks)
  • src/components/GraphqlAgent/graphqlAgent.tsx (1 hunks)
  • src/components/IndexerDetails/IndexerDetails.module.less (1 hunks)
  • src/components/IndexerDetails/IndexerDetails.tsx (2 hunks)
  • src/components/IndexerDetails/Row.tsx (5 hunks)
  • src/hooks/useChat.ts (1 hunks)
  • src/hooks/useProjects.ts (1 hunks)
  • src/hooks/useSpaceId.tsx (2 hunks)
  • src/lib/gtm.ts (1 hunks)
  • src/lib/utils.ts (1 hunks)
  • src/pages/explorer/Project/Project.tsx (3 hunks)
  • src/pages/explorer/Project/components/deploymentGraphqlAgent.tsx (1 hunks)
  • src/services/agent-api.ts (1 hunks)
  • src/types/index.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
src/hooks/useProjects.ts (2)
src/services/agent-api.ts (1)
  • projectsApi (53-107)
src/types/index.ts (2)
  • RegisterProjectRequest (23-27)
  • UpdateProjectConfigRequest (36-43)
src/hooks/useChat.ts (2)
src/types/index.ts (2)
  • ChatMessage (45-49)
  • ChatCompletionRequest (51-61)
src/services/agent-api.ts (2)
  • projectsApi (53-107)
  • chatApi (109-165)
src/pages/explorer/Project/components/deploymentGraphqlAgent.tsx (10)
src/hooks/useProjectFromQuery.tsx (1)
  • ProjectDetailsQuery (14-16)
src/hooks/useGetDeploymentManifest.tsx (1)
  • Manifest (13-32)
src/utils/index.tsx (2)
  • wrapProxyEndpoint (264-273)
  • notEmpty (72-79)
src/containers/Web3.tsx (1)
  • useAccount (33-42)
src/hooks/useProjects.ts (1)
  • useRegisterProject (22-31)
src/stores/project.ts (1)
  • useProjectStore (28-75)
src/types/index.ts (1)
  • ChatMessage (45-49)
src/hooks/useRequestServiceAgreementToken.ts (1)
  • useRequestServiceAgreementToken (10-77)
src/utils/limitation.ts (1)
  • makeCacheKey (18-28)
src/components/GraphqlAgent/graphqlAgent.tsx (1)
  • ChatInterface (178-428)
src/components/GraphqlAgent/graphqlAgent.tsx (5)
src/types/index.ts (1)
  • ChatMessage (45-49)
src/hooks/useProjects.ts (1)
  • useProject (14-20)
src/hooks/useChat.ts (1)
  • useChat (7-167)
src/lib/utils.ts (2)
  • formatCid (3-6)
  • formatTimestamp (8-22)
src/lib/gtm.ts (1)
  • gtmEvents (26-102)
src/pages/explorer/Project/Project.tsx (1)
src/router/routes.ts (1)
  • ROUTES (123-126)
src/services/agent-api.ts (1)
src/types/index.ts (8)
  • RegisterProjectRequest (23-27)
  • RegisterProjectResponse (29-34)
  • ProjectsListResponse (83-86)
  • ProjectConfig (10-21)
  • UpdateProjectConfigRequest (36-43)
  • ChatCompletionRequest (51-61)
  • ChatCompletionResponse (63-81)
  • HealthResponse (88-93)
src/components/IndexerDetails/Row.tsx (1)
src/utils/index.tsx (1)
  • notEmpty (72-79)
src/components/IndexerDetails/IndexerDetails.tsx (1)
src/utils/index.tsx (1)
  • notEmpty (72-79)
🪛 dotenv-linter (4.0.0)
.env.staging

[warning] 11-11: [UnorderedKey] The VITE_GRAPHQL_AGENT key should go before the VITE_IPFS_GATEWAY key

(UnorderedKey)

.env

[warning] 13-13: [UnorderedKey] The VITE_GRAPHQL_AGENT key should go before the VITE_IPFS_GATEWAY key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload results
  • GitHub Check: build
🔇 Additional comments (8)
src/components/GraphqlAgent/graphqlAgent.module.less (1)

1-473: LGTM! Well-structured CSS module.

The styling is comprehensive and well-organized with:

  • Proper semantic class naming
  • Good use of CSS variables for theming
  • Appropriate interactive states (hover, focus, disabled)
  • Responsive design considerations
  • Smooth transitions and animations
src/components/IndexerDetails/Row.tsx (3)

230-235: Agent-aware token flow looks correct.

The conditional logic properly handles two scenarios:

  1. User-initiated requests (isAgentRequest.current === false): Updates query limits and shows the playground UI
  2. Agent-initiated requests (isAgentRequest.current === true): Skips UI updates, allowing the agent to use the token silently

Moving setShowReqTokenConfirmModal(false) outside the conditional ensures the modal is always closed after token retrieval.


422-422: Good defensive programming with filter(notEmpty).

Filtering out null/undefined columns before passing to the Table component prevents potential runtime errors and ensures type safety.


113-113: The premise of this review comment is incorrect.

The isAgentRequest ref cannot be set to true by external code. ConnectedRow is a plain React.FC component (not wrapped with forwardRef), making the ref internal-only and inaccessible from parent components.

Consequently, the conditional at line 230 (if (!isAgentRequest.current)) will always execute its body, since the ref is initialized to false and never changed to true anywhere in the codebase. The ref assignment at line 378 (setting to false) appears redundant given the initial state.

Either this ref should be removed if unused, or the component architecture needs revision if external control of token behavior is intended.

Likely an incorrect or invalid review comment.

src/types/index.ts (1)

1-93: LGTM! Comprehensive and well-structured type definitions.

The interfaces provide clear contracts for:

  • Project lifecycle management (registration, configuration, updates)
  • Chat interactions (messages, completion requests/responses)
  • API health monitoring

All types are properly structured with appropriate optional fields and type constraints.

src/hooks/useProjects.ts (3)

22-31: LGTM! Proper cache invalidation on project registration.

The mutation correctly invalidates the projects list cache after successful registration, ensuring the UI reflects the new project.


33-44: Excellent cache management strategy.

The mutation uses both optimistic cache updates (setQueryData) and invalidation:

  1. Immediately updates the specific project cache for instant UI feedback
  2. Invalidates the projects list to ensure consistency

This provides the best user experience with instant updates while maintaining data integrity.


46-56: Proper cache cleanup on deletion.

Correctly removes the deleted project's query from the cache and invalidates the projects list to reflect the deletion across the UI.

VITE_AI_URL=https://ai-reaction-backend.subquery.network/ai-network/v1/chat/completions
VITE_AI_REACTION_URL=https://ai-reaction-backend.subquery.network
VITE_CONSUMER_CAMPAIGN_URL=https://consumer-campaign-api.subquery.network
VITE_GRAPHQL_AGENT=https://graphql-agent.subquery.network
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify environment variable consistency across all environment files.

The .env.staging file removed VITE_SUBQUERY_OFFICIAL_ETH_RPC, but src/hooks/useSpaceId.tsx still references it. Ensure this variable exists in the base .env file (not shown in the diff) to prevent staging environment failures.


🏁 Script executed:

#!/bin/bash
# Check if VITE_SUBQUERY_OFFICIAL_ETH_RPC is defined in the base .env file

if [ -f .env ]; then
  echo "=== Checking .env ==="
  grep -n "VITE_SUBQUERY_OFFICIAL_ETH_RPC" .env || echo "Variable not found in .env"
else
  echo ".env file not found"
fi

echo ""
echo "=== Checking all .env* files ==="
fd -t f '^\.env' --exec grep -l "VITE_SUBQUERY_OFFICIAL_ETH_RPC" {} \; || echo "Variable not found in any .env* files"

Length of output: 328



---

🏁 Script executed:

```shell
#!/bin/bash
# Re-run with corrected fd command to find all .env files including hidden ones
echo "=== Checking all .env* files (with hidden files) ==="
fd -H -t f '\.env' --exec grep -l "VITE_SUBQUERY_OFFICIAL_ETH_RPC" {} \;

echo ""
echo "=== Searching for VITE_SUBQUERY_OFFICIAL_ETH_RPC references in codebase ==="
rg -n "VITE_SUBQUERY_OFFICIAL_ETH_RPC" --type ts --type tsx --type js --type jsx

echo ""
echo "=== Display all .env files found ==="
fd -H -t f '\.env' | sort

Length of output: 229


🏁 Script executed:

#!/bin/bash
# Search with corrected rg syntax (tsx is part of ts type)
echo "=== Searching for VITE_SUBQUERY_OFFICIAL_ETH_RPC in TypeScript/JavaScript files ==="
rg -n "VITE_SUBQUERY_OFFICIAL_ETH_RPC" --type ts --type js

echo ""
echo "=== View content of .env.staging ==="
cat git/.env.staging 2>/dev/null || echo ".env.staging not readable"

echo ""
echo "=== View content of .env.testnet ==="
cat git/.env.testnet 2>/dev/null || echo ".env.testnet not readable"

echo ""
echo "=== Check if useSpaceId.tsx exists and its content ==="
find . -name "useSpaceId.tsx" -type f 2>/dev/null | head -1 | xargs cat 2>/dev/null || echo "useSpaceId.tsx not found or not readable"

Length of output: 3985


🏁 Script executed:

#!/bin/bash
# Look for all .env files at root and subdirectories
echo "=== All .env files in repository ==="
find . -maxdepth 3 -name '.env*' -type f 2>/dev/null | sort

echo ""
echo "=== Check if .env exists at root ==="
ls -la .env 2>/dev/null || echo ".env not found at root"

echo ""
echo "=== Search for VITE_SUBQUERY_OFFICIAL_ETH_RPC in ALL files ==="
rg "VITE_SUBQUERY_OFFICIAL_ETH_RPC" -n

echo ""
echo "=== Check other files that reference the variable ==="
rg -n "VITE_SUBQUERY_OFFICIAL_ETH_RPC" src/config/rainbowConf.tsx src/hooks/useEthersProvider.ts

Length of output: 893


🏁 Script executed:

#!/bin/bash
# Display the content of .env file
echo "=== Content of .env file ==="
cat .env

echo ""
echo "=== Search for VITE_SUBQUERY in .env ==="
grep "VITE_SUBQUERY" .env || echo "No VITE_SUBQUERY variables found"

echo ""
echo "=== Search for VITE_SUBQUERY in .env.staging ==="
grep "VITE_SUBQUERY" .env.staging || echo "No VITE_SUBQUERY variables found in .env.staging"

Length of output: 1004


VITE_SUBQUERY_OFFICIAL_ETH_RPC is missing from all environment files but actively used in code.

The concern is valid and critical. The variable VITE_SUBQUERY_OFFICIAL_ETH_RPC is referenced in four source files:

  • src/hooks/useSpaceId.tsx (lines 23, 49)
  • src/hooks/useEthersProvider.ts (line 62)
  • src/config/rainbowConf.tsx (line 32)

However, it is not defined in .env, .env.staging, or .env.testnet. This will cause runtime errors when the application attempts to access import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC and receives undefined.

Add VITE_SUBQUERY_OFFICIAL_ETH_RPC to the .env file (and any environment-specific files where needed), or handle the undefined case in the dependent code.

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 13-13: [UnorderedKey] The VITE_GRAPHQL_AGENT key should go before the VITE_IPFS_GATEWAY key

(UnorderedKey)

🤖 Prompt for AI Agents
In .env around line 13, VITE_SUBQUERY_OFFICIAL_ETH_RPC is missing though several
source files reference import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC; add this
environment variable to .env (and mirror into .env.staging and .env.testnet)
with the correct RPC URL value, or alternatively update the referenced code to
guard against undefined by providing a sensible default or throwing a clear
error when the variable is absent; ensure all environments used in CI/deploy
contain the new key.

VITE_SUBQUERY_OFFICIAL_BASE_RPC=https://base.rpc.subquery.network/public
VITE_SUBQUERY_OFFICIAL_ETH_RPC=https://ethereum.rpc.subquery.network/public
VITE_USE_SCANNER=1
VITE_GRAPHQL_AGENT=https://graphql-agent.subquery.network
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify that removed RPC environment variables are not referenced elsewhere.

The variables VITE_SUBQUERY_OFFICIAL_BASE_RPC and VITE_SUBQUERY_OFFICIAL_ETH_RPC were removed from this environment file. However, src/hooks/useSpaceId.tsx still references VITE_SUBQUERY_OFFICIAL_ETH_RPC at lines 23 and 49. This could result in undefined values being added to the RPC arrays, potentially breaking Web3 name resolution functionality.


🏁 Script executed:

#!/bin/bash
# Search for references to the removed environment variables

rg -n 'VITE_SUBQUERY_OFFICIAL_(BASE_RPC|ETH_RPC)' --type ts --type tsx -C 3

Length of output: 27


I need to retry the search with a corrected command. Let me search for these environment variable references using a different approach:


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for references to the removed environment variables without tsx file type
rg -n 'VITE_SUBQUERY_OFFICIAL_(BASE_RPC|ETH_RPC)' --type ts -C 3

Length of output: 2642


🏁 Script executed:

#!/bin/bash
# Also search directly in the hooks directory to be thorough
rg -n 'VITE_SUBQUERY_OFFICIAL' src/hooks/ -C 3

Length of output: 1412


🏁 Script executed:

#!/bin/bash
# Check if the specific file exists and examine it
if [ -f "src/hooks/useSpaceId.tsx" ]; then
  echo "=== File exists. Examining lines around 20-30 and 45-55 ==="
  sed -n '15,35p' src/hooks/useSpaceId.tsx | cat -n
  echo ""
  sed -n '40,60p' src/hooks/useSpaceId.tsx | cat -n
else
  echo "File src/hooks/useSpaceId.tsx not found"
fi

Length of output: 1726


Remove references to deleted environment variables or restore them to .env.staging.

The removed variables VITE_SUBQUERY_OFFICIAL_BASE_RPC and VITE_SUBQUERY_OFFICIAL_ETH_RPC are still referenced in at least four locations:

  • src/hooks/useSpaceId.tsx (lines 23, 49): Variable is used in RPC array and as a parameter
  • src/hooks/useEthersProvider.ts (lines 61-62): Both variables are cast to string and used for RPC endpoints
  • src/config/rainbowConf.tsx (lines 31-32): Both variables are passed directly to HTTP transport configuration
  • src/pages/bridge/index.tsx (line 147): VITE_SUBQUERY_OFFICIAL_BASE_RPC is used for provider configuration

Removing these without updating all references will result in undefined values being passed to RPC endpoints, breaking Web3 name resolution, provider initialization, and bridge functionality.

🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 11-11: [UnorderedKey] The VITE_GRAPHQL_AGENT key should go before the VITE_IPFS_GATEWAY key

(UnorderedKey)

"localforage": "^1.10.0",
"lodash-es": "^4.17.21",
"lru-cache": "^6.0.0",
"lucide-react": "^0.544.0",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the lucide-react version for security and currency.

Ensure that version ^0.544.0 is the latest stable release and has no known security vulnerabilities.


🏁 Script executed:

#!/bin/bash
# Check for the latest version and any security advisories for lucide-react

# Check npm for latest version
npm view lucide-react version

# Check for security advisories using GitHub CLI
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "lucide-react") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 57


Update lucide-react to the latest stable release (0.548.0).

The specified version 0.544.0 is not the latest stable release; version 0.548.0 is now available on npm. While the caret allows for updates within the minor/patch range, no known security vulnerabilities were found. Consider updating to the latest stable release to benefit from bug fixes and improvements.

🤖 Prompt for AI Agents
In package.json around line 56, the lucide-react dependency is pinned to
"^0.544.0"; update it to the latest stable release by changing the version to
"^0.548.0" and then run your package manager to update the lockfile (npm install
or yarn install) so the lockfile and node_modules reflect the new version; also
run tests/build to verify no regressions.

Comment on lines +49 to +64
const conversationHistory = [
...messages.map((msg) => ({
role: msg.role,
content: msg.content,
timestamp: msg.timestamp,
})),
{ role: 'user', content, timestamp: Date.now() },
];

const request: ChatCompletionRequest = {
model: 'gpt-4o-mini',
messages: conversationHistory,
stream: false,
think: true, // Default to true for non-streaming
};

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Strip timestamps before building the completion payload.

Line 58 currently forwards the entire ChatMessage objects (including the timestamp) to the completions endpoint. The API schema for ChatCompletionRequest.messages only permits { role, content }, so this payload can be rejected by the backend—note the streaming path already maps to the slimmer shape. Please drop the timestamp field before sending.

Apply this diff:

-      const conversationHistory = [
-        ...messages.map((msg) => ({
-          role: msg.role,
-          content: msg.content,
-          timestamp: msg.timestamp,
-        })),
-        { role: 'user', content, timestamp: Date.now() },
-      ];
+      const conversationHistory = [
+        ...messages.map(({ role, content }) => ({
+          role,
+          content,
+        })),
+        { role: 'user', content },
+      ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const conversationHistory = [
...messages.map((msg) => ({
role: msg.role,
content: msg.content,
timestamp: msg.timestamp,
})),
{ role: 'user', content, timestamp: Date.now() },
];
const request: ChatCompletionRequest = {
model: 'gpt-4o-mini',
messages: conversationHistory,
stream: false,
think: true, // Default to true for non-streaming
};
const conversationHistory = [
...messages.map(({ role, content }) => ({
role,
content,
})),
{ role: 'user', content },
];
const request: ChatCompletionRequest = {
model: 'gpt-4o-mini',
messages: conversationHistory,
stream: false,
think: true, // Default to true for non-streaming
};
🤖 Prompt for AI Agents
In src/hooks/useChat.ts around lines 49 to 64, the completion payload currently
includes the ChatMessage.timestamp which is not allowed by the
ChatCompletionRequest.messages schema; remove the timestamp when building
conversationHistory so each message is only { role, content } (mirror the
streaming path), then build request.messages from that sanitized array (keep
adding the new user message without timestamp); ensure no other fields beyond
role and content are forwarded to the request.

Comment on lines +84 to +166
// Non-streaming chat mutation
const chatMutation = useMutation({
mutationFn: sendMessage,
});

// Streaming chat
const sendStreamingMessage = useCallback(
async (content: string, dataLimit: number = 10, showThink: boolean = true, tempEndpoint = '', tempToken = '') => {
const userMessage: ChatMessage = {
role: 'user',
content,
timestamp: Date.now(),
};

addMessage(userMessage);
setIsStreaming(true);

// Add empty assistant message that will be updated
const assistantMessage: ChatMessage = {
role: 'assistant',
content: '',
timestamp: Date.now(),
};
addMessage(assistantMessage);

try {
// Build conversation history including the new user message
// Add data limit instruction to the last user message for backend, but keep UI clean
const enhancedContent = `${content}\n\n[SYSTEM: When constructing GraphQL queries, limit results to maximum ${dataLimit} records using appropriate pagination (first: ${dataLimit} or limit: ${dataLimit})]`;

const conversationHistory = [
...messages.map((msg) => ({
role: msg.role,
content: msg.content,
timestamp: msg.timestamp,
})),
{ role: 'user', content: enhancedContent, timestamp: Date.now() },
];

const request: ChatCompletionRequest = {
model: 'gpt-4o-mini',
messages: conversationHistory.map((msg) => ({ role: msg.role, content: msg.content })),
stream: true,
think: showThink,
tempEndpoint,
tempToken,
};

const stream = chatApi.streamChat(projectCid, request);

for await (const chunk of stream) {
updateLastMessage(chunk);
}
} catch (error) {
console.error('Streaming chat error:', error);

// Update the last message with error
onMessagesChange((prevMessages) => {
const updated = [...prevMessages];
const lastIndex = updated.length - 1;
if (lastIndex >= 0 && updated[lastIndex].role === 'assistant') {
updated[lastIndex] = {
...updated[lastIndex],
content: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
};
}
return updated;
});
} finally {
setIsStreaming(false);
}
},
[projectCid, addMessage, updateLastMessage, messages],
);

return {
isStreaming,
isLoading: chatMutation.isPending,
error: chatMutation.error,
sendMessage,
sendStreamingMessage,
addMessage,
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wire sendMessage through the React Query mutation.

useMutation never transitions state because callers invoke the raw sendMessage callback, bypassing mutate/mutateAsync. As a result, isLoading and error remain stuck at their defaults, breaking any UI that relies on them. Expose a wrapper that delegates to chatMutation.mutateAsync instead.

Apply this diff:

   const chatMutation = useMutation({
     mutationFn: sendMessage,
   });
+
+  const sendMessageWithStatus = useCallback(
+    (content: string) => chatMutation.mutateAsync(content),
+    [chatMutation],
+  );
 
   // Streaming chat
   const sendStreamingMessage = useCallback(
@@
   return {
     isStreaming,
     isLoading: chatMutation.isPending,
     error: chatMutation.error,
-    sendMessage,
+    sendMessage: sendMessageWithStatus,
     sendStreamingMessage,
     addMessage,
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Non-streaming chat mutation
const chatMutation = useMutation({
mutationFn: sendMessage,
});
// Streaming chat
const sendStreamingMessage = useCallback(
async (content: string, dataLimit: number = 10, showThink: boolean = true, tempEndpoint = '', tempToken = '') => {
const userMessage: ChatMessage = {
role: 'user',
content,
timestamp: Date.now(),
};
addMessage(userMessage);
setIsStreaming(true);
// Add empty assistant message that will be updated
const assistantMessage: ChatMessage = {
role: 'assistant',
content: '',
timestamp: Date.now(),
};
addMessage(assistantMessage);
try {
// Build conversation history including the new user message
// Add data limit instruction to the last user message for backend, but keep UI clean
const enhancedContent = `${content}\n\n[SYSTEM: When constructing GraphQL queries, limit results to maximum ${dataLimit} records using appropriate pagination (first: ${dataLimit} or limit: ${dataLimit})]`;
const conversationHistory = [
...messages.map((msg) => ({
role: msg.role,
content: msg.content,
timestamp: msg.timestamp,
})),
{ role: 'user', content: enhancedContent, timestamp: Date.now() },
];
const request: ChatCompletionRequest = {
model: 'gpt-4o-mini',
messages: conversationHistory.map((msg) => ({ role: msg.role, content: msg.content })),
stream: true,
think: showThink,
tempEndpoint,
tempToken,
};
const stream = chatApi.streamChat(projectCid, request);
for await (const chunk of stream) {
updateLastMessage(chunk);
}
} catch (error) {
console.error('Streaming chat error:', error);
// Update the last message with error
onMessagesChange((prevMessages) => {
const updated = [...prevMessages];
const lastIndex = updated.length - 1;
if (lastIndex >= 0 && updated[lastIndex].role === 'assistant') {
updated[lastIndex] = {
...updated[lastIndex],
content: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
};
}
return updated;
});
} finally {
setIsStreaming(false);
}
},
[projectCid, addMessage, updateLastMessage, messages],
);
return {
isStreaming,
isLoading: chatMutation.isPending,
error: chatMutation.error,
sendMessage,
sendStreamingMessage,
addMessage,
};
// Non-streaming chat mutation
const chatMutation = useMutation({
mutationFn: sendMessage,
});
const sendMessageWithStatus = useCallback(
(content: string) => chatMutation.mutateAsync(content),
[chatMutation],
);
// Streaming chat
const sendStreamingMessage = useCallback(
async (content: string, dataLimit: number = 10, showThink: boolean = true, tempEndpoint = '', tempToken = '') => {
const userMessage: ChatMessage = {
role: 'user',
content,
timestamp: Date.now(),
};
addMessage(userMessage);
setIsStreaming(true);
// Add empty assistant message that will be updated
const assistantMessage: ChatMessage = {
role: 'assistant',
content: '',
timestamp: Date.now(),
};
addMessage(assistantMessage);
try {
// Build conversation history including the new user message
// Add data limit instruction to the last user message for backend, but keep UI clean
const enhancedContent = `${content}\n\n[SYSTEM: When constructing GraphQL queries, limit results to maximum ${dataLimit} records using appropriate pagination (first: ${dataLimit} or limit: ${dataLimit})]`;
const conversationHistory = [
...messages.map((msg) => ({
role: msg.role,
content: msg.content,
timestamp: msg.timestamp,
})),
{ role: 'user', content: enhancedContent, timestamp: Date.now() },
];
const request: ChatCompletionRequest = {
model: 'gpt-4o-mini',
messages: conversationHistory.map((msg) => ({ role: msg.role, content: msg.content })),
stream: true,
think: showThink,
tempEndpoint,
tempToken,
};
const stream = chatApi.streamChat(projectCid, request);
for await (const chunk of stream) {
updateLastMessage(chunk);
}
} catch (error) {
console.error('Streaming chat error:', error);
// Update the last message with error
onMessagesChange((prevMessages) => {
const updated = [...prevMessages];
const lastIndex = updated.length - 1;
if (lastIndex >= 0 && updated[lastIndex].role === 'assistant') {
updated[lastIndex] = {
...updated[lastIndex],
content: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
};
}
return updated;
});
} finally {
setIsStreaming(false);
}
},
[projectCid, addMessage, updateLastMessage, messages],
);
return {
isStreaming,
isLoading: chatMutation.isPending,
error: chatMutation.error,
sendMessage: sendMessageWithStatus,
sendStreamingMessage,
addMessage,
};

'https://ethereum.publicnode.com',
'https://eth.merkle.io',
'https://eth.drpc.org',
import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Undefined environment variable will break RPC functionality.

This line adds import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC to the rpcMainnet array. However, according to the changes in .env.staging, the VITE_SUBQUERY_OFFICIAL_ETH_RPC variable has been removed. This will result in undefined being added to the array, which could cause runtime errors when this RPC URL is randomly selected on line 57.

Consider either:

  1. Re-adding the environment variable to all environment files (.env, .env.staging), or
  2. Removing this line if the RPC endpoint is no longer needed

Apply this diff if removing the reference:

 const rpcMainnet = [
   'https://eth.llamarpc.com',
   'https://rpc.payload.de',
   'https://ethereum.publicnode.com',
   'https://eth.merkle.io',
   'https://eth.drpc.org',
-  import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC,
 ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC,
const rpcMainnet = [
'https://eth.llamarpc.com',
'https://rpc.payload.de',
'https://ethereum.publicnode.com',
'https://eth.merkle.io',
'https://eth.drpc.org',
];
🤖 Prompt for AI Agents
In src/hooks/useSpaceId.tsx around line 23, the code adds
import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC to the rpcMainnet array but that
env var was removed and may be undefined; remove that line from the rpcMainnet
array (and optionally add a defensive filter to remove falsy values from all rpc
arrays) so undefined RPC URLs cannot be selected at runtime.

// it seems if this url is invalid, then the URL passed by getDomainName will not be used.
createWeb3Name({
rpcUrl: 'https://ethereum.rpc.subquery.network/public',
rpcUrl: import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Undefined RPC URL in createWeb3Name configuration.

The rpcUrl parameter is set to import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC, which is undefined after the environment variable was removed in .env.staging. This could cause the Web3Name SDK to malfunction.

According to the comment on line 47, if the initial rpcUrl is invalid, the SDK may not use the URLs passed to getDomainName. You should either:

  1. Restore the environment variable in all environment files, or
  2. Use one of the hardcoded RPC URLs as a fallback

Example fix using a fallback:

 const web3Name = useMemo(
   () =>
     // it seems if this url is invalid, then the URL passed by getDomainName will not be used.
     createWeb3Name({
-      rpcUrl: import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC,
+      rpcUrl: import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC || 'https://eth.llamarpc.com',
     }),
   [],
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rpcUrl: import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC,
rpcUrl: import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC || 'https://eth.llamarpc.com',
🤖 Prompt for AI Agents
In src/hooks/useSpaceId.tsx around line 49, the createWeb3Name configuration
sets rpcUrl to import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC which is undefined
in staging; update this to provide a stable RPC by either restoring that env var
across .env files or adding a fallback (choose one of the existing hardcoded RPC
URLs) so rpcUrl is never undefined; implement the change by replacing the single
env value with a conditional that uses the env var if present otherwise a
hardcoded RPC string so the SDK always receives a valid rpcUrl.

Comment on lines +24 to +25
import { Select } from 'antd';
import { Option } from 'antd/es/mentions';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix Select options import

Option from antd/es/mentions is unrelated to the Select widget, so the dropdown never receives real options. As a result, the operator list doesn’t render or register selection events, blocking users from ever choosing an indexer and making the agent unusable. Swap to Select.Option (or destructure from Select) so Ant Design wires things up correctly.

-import { Select } from 'antd';
-import { Option } from 'antd/es/mentions';
+import { Select } from 'antd';-                {indexers.map((i) => (
-                  <Option key={i.indexerId}>
+                {indexers.map((i) => (
+                  <Select.Option key={i.indexerId} value={i.indexerId}>
                     <IndexerOption
                       deploymentId={deploymentId || ''}
                       indexerId={i.indexerId}
                       onClick={async (url, proxyUrl) => {
…
-                    ></IndexerOption>
-                  </Option>
+                    ></IndexerOption>
+                  </Select.Option>
                 ))}

Also applies to: 159-199

🤖 Prompt for AI Agents
In src/pages/explorer/Project/components/deploymentGraphqlAgent.tsx around lines
24-25 (and similarly lines 159-199), the code imports Option from
'antd/es/mentions' which is not the Select.Option used by Ant Design; replace
that import by using Select.Option (or destructure Option from Select) so the
Select receives proper Option components. Update imports to only import Select
from 'antd' then change JSX to use <Select.Option ...> (or const { Option } =
Select) for all option usages within the noted ranges so the dropdown renders
options and selection events work correctly.

Comment on lines +181 to +185
await registerProject.mutateAsync({
cid: deploymentId || '',
endpoint: queryUrl || '',
authorization: `Bearer ${token.data}`,
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use the freshly selected endpoint when registering the project

In the cached-token branch we call registerProject.mutateAsync with endpoint: queryUrl || ''. Because queryUrl still holds the previous render’s state (initially an empty string), the mutation sends an empty endpoint the first time a cached token is reused, so the project isn’t registered against the operator and subsequent chat calls fail. Use the url argument we just computed for this operator instead.

-                              await registerProject.mutateAsync({
-                                cid: deploymentId || '',
-                                endpoint: queryUrl || '',
-                                authorization: `Bearer ${token.data}`,
-                              });
+                              await registerProject.mutateAsync({
+                                cid: deploymentId || '',
+                                endpoint: url,
+                                authorization: `Bearer ${token.data}`,
+                              });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await registerProject.mutateAsync({
cid: deploymentId || '',
endpoint: queryUrl || '',
authorization: `Bearer ${token.data}`,
});
await registerProject.mutateAsync({
cid: deploymentId || '',
endpoint: url,
authorization: `Bearer ${token.data}`,
});
🤖 Prompt for AI Agents
In src/pages/explorer/Project/components/deploymentGraphqlAgent.tsx around lines
181 to 185, the registerProject.mutateAsync call uses endpoint: queryUrl || ''
which can be stale on re-use of a cached token; replace the endpoint argument
with the freshly computed url variable (endpoint: url) so the mutation sends the
newly selected operator endpoint rather than the previous render's queryUrl.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@HuberTRoy HuberTRoy merged commit d71b4a5 into main Oct 30, 2025
3 checks passed
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.

3 participants