-
Notifications
You must be signed in to change notification settings - Fork 20
Feat/agent #874
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/agent #874
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit 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)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
Comment |
There was a problem hiding this 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 || '', |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
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 || ''.
| endpoint: queryUrl || '', | |
| endpoint: url, |
| 'https://ethereum.publicnode.com', | ||
| 'https://eth.merkle.io', | ||
| 'https://eth.drpc.org', | ||
| import.meta.env.VITE_SUBQUERY_OFFICIAL_ETH_RPC, |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
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.
| loading?: boolean; | ||
| } | ||
|
|
||
| // 新增:可折叠的think块组件 |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
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.
| // 新增:可折叠的think块组件 | |
| // Added: Collapsible think block component |
| const lastContentUpdateRef = useRef(Date.now()); | ||
| const autoScrollTimeoutRef = useRef<ReturnType<typeof setTimeout>>(); | ||
|
|
||
| // 检测用户是否在底部附近(阈值为30px) |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
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.
| // 检测用户是否在底部附近(阈值为30px) | |
| // Check if the user is near the bottom (threshold: 30px) |
| return scrollHeight - scrollTop - clientHeight < 30; | ||
| }; | ||
|
|
||
| // 滚动到底部的函数 |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
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.
| // 滚动到底部的函数 | |
| // Function to scroll to the bottom |
| @@ -0,0 +1,63 @@ | |||
| import { type ClassValue, clsx } from 'clsx'; | |||
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import clsx.
| import { type ClassValue, clsx } from 'clsx'; |
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import * as React from 'react'; | ||
| import { useMemo } from 'react'; |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import useMemo.
| import { useMemo } from 'react'; |
| import { IndexerName } from '@components/IndexerDetails/IndexerName'; | ||
| import { useAccount } from '@containers/Web3'; | ||
| import { useIndexerMetadata } from '@hooks'; | ||
| import { useConsumerHostServices } from '@hooks/useConsumerHostServices'; |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import useConsumerHostServices.
| import { useConsumerHostServices } from '@hooks/useConsumerHostServices'; |
| useAsyncMemo, | ||
| useGetDeploymentIndexersLazyQuery, | ||
| useGetIndexerDeploymentLazyQuery, |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused imports useAsyncMemo, useGetIndexerDeploymentLazyQuery.
| useAsyncMemo, | |
| useGetDeploymentIndexersLazyQuery, | |
| useGetIndexerDeploymentLazyQuery, | |
| useGetDeploymentIndexersLazyQuery, |
There was a problem hiding this 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
.agentModalclass 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:
- Whether a CSS variable exists in the design system for the black color value
- 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
⛔ Files ignored due to path filters (1)
yarn.lockis 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:
- User-initiated requests (
isAgentRequest.current === false): Updates query limits and shows the playground UI- Agent-initiated requests (
isAgentRequest.current === true): Skips UI updates, allowing the agent to use the token silentlyMoving
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
isAgentRequestref cannot be set totrueby external code.ConnectedRowis a plainReact.FCcomponent (not wrapped withforwardRef), 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 tofalseand never changed totrueanywhere in the codebase. The ref assignment at line 378 (setting tofalse) 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:
- Immediately updates the specific project cache for instant UI feedback
- 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.tsLength 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 3Length 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 3Length 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"
fiLength 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 parametersrc/hooks/useEthersProvider.ts(lines 61-62): Both variables are cast to string and used for RPC endpointssrc/config/rainbowConf.tsx(lines 31-32): Both variables are passed directly to HTTP transport configurationsrc/pages/bridge/index.tsx(line 147):VITE_SUBQUERY_OFFICIAL_BASE_RPCis 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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
| 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 | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| // 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, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| // 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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- Re-adding the environment variable to all environment files (.env, .env.staging), or
- 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.
| 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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- Restore the environment variable in all environment files, or
- 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.
| 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.
| import { Select } from 'antd'; | ||
| import { Option } from 'antd/es/mentions'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| await registerProject.mutateAsync({ | ||
| cid: deploymentId || '', | ||
| endpoint: queryUrl || '', | ||
| authorization: `Bearer ${token.data}`, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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>
Description
Type of change
Please delete options that are not relevant.
Summary by CodeRabbit
New Features
Chores