Skip to content

Commit 0653108

Browse files
authored
Merge pull request #6355 from remix-project-org/modular_conversation_starters
dynamic conversation starters
2 parents f6c93a0 + 127ee1b commit 0653108

File tree

3 files changed

+126
-19
lines changed

3 files changed

+126
-19
lines changed

apps/remix-ide-e2e/src/tests/pinned_plugin.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ module.exports = {
1111
'Check if RemixAI plugin is pinned #group1': function (browser: NightwatchBrowser) {
1212
browser
1313
.waitForElementVisible('*[data-id="movePluginToLeft"]')
14-
.waitForElementVisible('*[data-id="remix-ai-assistant-starter-0"]')
14+
.waitForElementVisible('*[data-id="remix-ai-assistant-starter-beginner-0"]')
15+
.waitForElementVisible('*[data-id="remix-ai-assistant-starter-intermediate-1"]')
16+
.waitForElementVisible('*[data-id="remix-ai-assistant-starter-expert-2"]')
1517
.click('*[data-id="movePluginToLeft"]')
1618
.waitForElementVisible('*[data-pinnedPlugin="movePluginToRight-remixaiassistant"]')
1719
},

libs/remix-ui/remix-ai-assistant/src/components/chat.tsx

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@ import ReactMarkdown from "react-markdown"
22
import remarkGfm from "remark-gfm"
33
import copy from "copy-to-clipboard"
44
import { ChatMessage, assistantAvatar } from "../lib/types"
5-
import React from 'react'
6-
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
7-
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
5+
import React, { useState, useEffect } from 'react'
86
import { CustomTooltip } from "@remix-ui/helper"
9-
10-
const DEFAULT_SUGGESTIONS = [
11-
'What is a modifier?',
12-
'What is a Uniswap hook?',
13-
'What is a ZKP?'
14-
]
7+
import {
8+
sampleConversationStarters,
9+
type ConversationStarter
10+
} from "../lib/conversationStarters"
1511

1612
// ChatHistory component
1713
export interface ChatHistoryComponentProps {
@@ -22,24 +18,43 @@ export interface ChatHistoryComponentProps {
2218
historyRef: React.RefObject<HTMLDivElement>
2319
}
2420

25-
const AiChatIntro = (props) => {
21+
interface AiChatIntroProps {
22+
sendPrompt: (prompt: string) => void
23+
}
24+
25+
const AiChatIntro: React.FC<AiChatIntroProps> = ({ sendPrompt }) => {
26+
const [conversationStarters, setConversationStarters] = useState<ConversationStarter[]>([])
27+
28+
useEffect(() => {
29+
// Sample new conversation starters when component mounts
30+
const starters = sampleConversationStarters()
31+
setConversationStarters(starters)
32+
}, [])
33+
34+
const refreshStarters = () => {
35+
const newStarters = sampleConversationStarters()
36+
setConversationStarters(newStarters)
37+
}
38+
2639
return (
2740
<div className="assistant-landing d-flex flex-column mx-1 align-items-center justify-content-center text-center h-100 w-100">
2841
<img src={assistantAvatar} alt="RemixAI logo" style={{ width: '120px' }} className="mb-3 container-img" />
2942
<h5 className="mb-2">RemixAI</h5>
3043
<p className="mb-4" style={{ fontSize: '0.9rem' }}>
31-
RemixAI provides you personalized guidance as you build. It can break down concepts,
32-
answer questions about blockchain technology and assist you with your smart contracts.
44+
RemixAI provides you personalized guidance as you build. It can break down concepts,
45+
answer questions about blockchain technology and assist you with your smart contracts.
3346
</p>
34-
<div className="d-flex flex-column mt-3">
35-
{DEFAULT_SUGGESTIONS.map((s, index) => (
47+
{/* Dynamic Conversation Starters */}
48+
<div className="d-flex flex-column mt-3" style={{ maxWidth: '400px' }}>
49+
{conversationStarters.map((starter, index) => (
50+
3651
<button
37-
key={s}
38-
data-id={`remix-ai-assistant-starter-${index}`}
52+
key={`${starter.level}-${index}`}
53+
data-id={`remix-ai-assistant-starter-${starter.level}-${index}`}
3954
className="btn btn-secondary mb-2 w-100 text-start"
40-
onClick={() => props.sendPrompt(s)}
55+
onClick={() => sendPrompt(starter.question)}
4156
>
42-
<i className="fa-kit fa-remixai me-2"></i>{s}
57+
{starter.question}
4358
</button>
4459
))}
4560
</div>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
export interface ConversationStarter {
2+
question: string;
3+
level: 'beginner' | 'intermediate' | 'expert';
4+
category: string;
5+
}
6+
7+
export const CONVERSATION_STARTERS: ConversationStarter[] = [
8+
// Beginner Level (20 Questions)
9+
{ question: "", level: "beginner", category: "programming" },
10+
{ question: "How to use blob storage?", level: "beginner", category: "Solidity" },
11+
{ question: "What is the difference between storage, memory, and calldata in Solidity?", level: "beginner", category: "Solidity" },
12+
{ question: "How are dynamic arrays stored in contract storage?", level: "beginner", category: "Solidity" },
13+
{ question: "How does delegatecall differ from call? ", level: "beginner", category: "Solidity" },
14+
{ question: "How to avoid using dynamic array in Solidity?", level: "beginner", category: "Solidity" },
15+
{ question: "List some gas saving techniques", level: "beginner", category: "Solidity" },
16+
{ question: "How do NFTs work?", level: "beginner", category: "blockchain" },
17+
{ question: "Debugging strategies?", level: "beginner", category: "development" },
18+
19+
// Intermediate Level (20 Questions)
20+
{ question: "What’s a Uniswap hook?", level: "intermediate", category: "DeFi" },
21+
{ question: "How to use 1inch?", level: "intermediate", category: "DeFi" },
22+
{ question: "Show a contract that includes a flash loan", level: "intermediate", category: "DeFi" },
23+
{ question: "Show a smart contract that records carbon credits", level: "intermediate", category: "blockchain" },
24+
{ question: "Show a sybil-resistant voting contract", level: "intermediate", category: "programming" },
25+
26+
// Expert Level (20 Questions)
27+
{ question: "Account abstraction impact on UX?", level: "expert", category: "blockchain" },
28+
{ question: "MEV protection strategies?", level: "expert", category: "DeFi" },
29+
{ question: "ZK-rollups vs optimistic rollups?", level: "expert", category: "blockchain" },
30+
{ question: "Formal verification tools worth it?", level: "expert", category: "development" },
31+
{ question: "What is the power of tau?", level: "expert", category: "ZK" },
32+
{ question: "Groth16 vs Plonk?", level: "expert", category: "ZK" },
33+
{ question: "Cross-chain messaging protocols?", level: "expert", category: "blockchain" },
34+
{ question: "EIP-4844 blob space economics?", level: "expert", category: "blockchain" },
35+
{ question: "Restaking security assumptions?", level: "expert", category: "blockchain" },
36+
{ question: "AI-assisted smart contract auditing?", level: "expert", category: "development" },
37+
{ question: "Maximal extractable value mitigation?", level: "expert", category: "blockchain" },
38+
{ question: "Explain a witness in a ZK circuit", level: "expert", category: "ZK" },
39+
{ question: "Explain a rate limiting nullifier", level: "expert", category: "blockchain" },
40+
{ question: "Proto-danksharding readiness?", level: "expert", category: "blockchain" },
41+
{ question: "Homomorphic encryption in web3?", level: "expert", category: "blockchain" },
42+
{ question: "Explain the UUPS upgradeable contract", level: "expert", category: "blockchain" },
43+
{ question: "Explain the Diamond Pattern", level: "expert", category: "blockchain" },
44+
{ question: "Explain an underflow in Solidity", level: "expert", category: "blockchain" },
45+
{ question: "What are some tools that can help with security?", level: "expert", category: "blockchain" },
46+
{ question: "Explain the Transparent upgradeable contract", level: "expert", category: "blockchain" },
47+
{ question: "What the difference between an ERC and an EIP?", level: "expert", category: "blockchain" },
48+
{ question: "How to work with EIP 7702?", level: "expert", category: "blockchain" },
49+
{ question: "How to work a EIP 4337 Smart Account", level: "expert", category: "blockchain" },
50+
];
51+
52+
/**
53+
* Randomly samples one question from each difficulty level
54+
* @returns An array of 3 conversation starters (beginner, intermediate, expert)
55+
*/
56+
export function sampleConversationStarters(): ConversationStarter[] {
57+
const beginnerQuestions = CONVERSATION_STARTERS.filter(q => q.level === 'beginner');
58+
const intermediateQuestions = CONVERSATION_STARTERS.filter(q => q.level === 'intermediate');
59+
const expertQuestions = CONVERSATION_STARTERS.filter(q => q.level === 'expert');
60+
61+
const randomBeginner = beginnerQuestions[Math.floor(Math.random() * beginnerQuestions.length)];
62+
const randomIntermediate = intermediateQuestions[Math.floor(Math.random() * intermediateQuestions.length)];
63+
const randomExpert = expertQuestions[Math.floor(Math.random() * expertQuestions.length)];
64+
65+
return [randomBeginner, randomIntermediate, randomExpert];
66+
}
67+
68+
/**
69+
* Gets conversation starters with seeded randomization for consistent results in same session
70+
* @param seed Optional seed for reproducible randomization
71+
* @returns An array of 3 conversation starters (beginner, intermediate, expert)
72+
*/
73+
export function sampleConversationStartersWithSeed(seed?: number): ConversationStarter[] {
74+
const seededRandom = (seedValue: number) => {
75+
const x = Math.sin(seedValue) * 10000;
76+
return x - Math.floor(x);
77+
};
78+
79+
const actualSeed = seed ?? Date.now();
80+
const beginnerQuestions = CONVERSATION_STARTERS.filter(q => q.level === 'beginner');
81+
const intermediateQuestions = CONVERSATION_STARTERS.filter(q => q.level === 'intermediate');
82+
const expertQuestions = CONVERSATION_STARTERS.filter(q => q.level === 'expert');
83+
84+
const randomBeginner = beginnerQuestions[Math.floor(seededRandom(actualSeed) * beginnerQuestions.length)];
85+
const randomIntermediate = intermediateQuestions[Math.floor(seededRandom(actualSeed + 1) * intermediateQuestions.length)];
86+
const randomExpert = expertQuestions[Math.floor(seededRandom(actualSeed + 2) * expertQuestions.length)];
87+
88+
return [randomBeginner, randomIntermediate, randomExpert];
89+
}
90+

0 commit comments

Comments
 (0)