generated from sendaifun/solana-agent-kit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from plutohan/init-client
Initialize chatbot client
- Loading branch information
Showing
32 changed files
with
5,948 additions
and
1,059 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "next/core-web-vitals" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { NextRequest, NextResponse } from "next/server"; | ||
import { ChatOpenAI } from "@langchain/openai"; | ||
import { MemorySaver } from "@langchain/langgraph"; | ||
import { createReactAgent } from "@langchain/langgraph/prebuilt"; | ||
import { SolanaAgentKit, createSolanaTools } from "../../../src/index"; | ||
|
||
const llm = new ChatOpenAI({ | ||
temperature: 0.7, | ||
model: "gpt-4o", | ||
}); | ||
|
||
const solanaAgent = new SolanaAgentKit( | ||
process.env.SOLANA_PRIVATE_KEY!, | ||
process.env.RPC_URL!, | ||
{ | ||
OPENAI_API_KEY: process.env.OPENAI_API_KEY!, | ||
COINGECKO_DEMO_API_KEY: process.env.COINGECKO_DEMO_API_KEY!, | ||
}, | ||
); | ||
|
||
const tools = createSolanaTools(solanaAgent); | ||
const memory = new MemorySaver(); | ||
|
||
// Cast the agent to the new type | ||
const agent = createReactAgent({ | ||
llm, | ||
tools, | ||
checkpointSaver: memory, | ||
messageModifier: ` | ||
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are | ||
empowered to interact onchain using your tools. If you ever need funds, you can request them from the | ||
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX | ||
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you | ||
can't do with your currently available tools, you must say so, and encourage them to implement it | ||
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be | ||
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested. | ||
`, | ||
}); | ||
|
||
export async function POST(req: NextRequest) { | ||
try { | ||
const body = await req.json(); | ||
const messages = body.messages ?? []; | ||
|
||
console.log(messages); | ||
|
||
const eventStream = agent.streamEvents( | ||
{ | ||
messages, | ||
}, | ||
{ | ||
version: "v2", | ||
configurable: { | ||
thread_id: "Solana Agent Kit!", | ||
}, | ||
}, | ||
); | ||
console.log("eventStream", eventStream); | ||
const textEncoder = new TextEncoder(); | ||
const transformStream = new ReadableStream({ | ||
async start(controller) { | ||
for await (const { event, data } of eventStream) { | ||
if (event === "on_chat_model_stream") { | ||
if (data.chunk.content) { | ||
controller.enqueue(textEncoder.encode(data.chunk.content)); | ||
} | ||
} | ||
} | ||
controller.close(); | ||
}, | ||
}); | ||
|
||
console.log("transformStream", transformStream); | ||
|
||
return new Response(transformStream); | ||
} catch (e: any) { | ||
console.log("error", e); | ||
return NextResponse.json({ error: e.message }, { status: e.status ?? 500 }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
@tailwind base; | ||
@tailwind components; | ||
@tailwind utilities; | ||
|
||
body { | ||
color: #f8f8f8; | ||
background: #131318; | ||
} | ||
|
||
body input, | ||
body textarea { | ||
color: black; | ||
} | ||
|
||
a { | ||
color: #2d7bd4; | ||
} | ||
|
||
a:hover { | ||
border-bottom: 1px solid; | ||
} | ||
|
||
p { | ||
margin: 8px 0; | ||
} | ||
|
||
code { | ||
color: #ffa500; | ||
} | ||
|
||
li { | ||
padding: 4px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import "./globals.css"; | ||
import { Public_Sans } from "next/font/google"; | ||
|
||
const publicSans = Public_Sans({ subsets: ["latin"] }); | ||
|
||
export default function RootLayout({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) { | ||
return ( | ||
<html lang="en"> | ||
<head> | ||
<title>SolanaAgentKit + LangChain + Next.js Template</title> | ||
<link rel="shortcut icon" href="/images/favicon.ico" /> | ||
<meta | ||
name="description" | ||
content="Starter template showing how to use SolanaAgentKit with Langchain in Next.js projects." | ||
/> | ||
<meta | ||
property="og:title" | ||
content="SolanaAgentKit + LangChain + Next.js Template" | ||
/> | ||
<meta | ||
property="og:description" | ||
content="Starter template showing how to use SolanaAgentKit with LangChain in Next.js projects." | ||
/> | ||
<meta property="og:image" content="/images/title-card.png" /> | ||
<meta name="twitter:card" content="summary_large_image" /> | ||
<meta | ||
name="twitter:title" | ||
content="SolanaAgentKit + LangChain + Next.js Template" | ||
/> | ||
<meta | ||
name="twitter:description" | ||
content="Starter template showing how to use SolanaAgentKit with LangChain in Next.js projects." | ||
/> | ||
<meta name="twitter:image" content="/images/title-card.png" /> | ||
</head> | ||
<body className={publicSans.className}> | ||
<div className="flex flex-col p-4 md:p-12 h-[100vh]">{children}</div> | ||
</body> | ||
</html> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { ChatWindow } from "@/components/ChatWindow"; | ||
|
||
export default function Home() { | ||
const InfoCard = ( | ||
<div className="p-4 md:p-8 rounded bg-[#25252d] w-full max-h-[85%] overflow-hidden"> | ||
<h1 className="text-3xl md:text-4xl mb-4"> | ||
SolanaAgentKit + LangChain.js 🦜🔗 + Next.js | ||
</h1> | ||
<ul> | ||
<li className="text-l"> | ||
🤝 | ||
<span className="ml-2"> | ||
This template showcases a simple agent chatbot using{" "} | ||
<a href="https://www.solanaagentkit.xyz/">SolanaAgentKit</a> | ||
{", "} | ||
<a href="https://js.langchain.com/" target="_blank"> | ||
LangChain.js | ||
</a>{" "} | ||
and the Vercel{" "} | ||
<a href="https://sdk.vercel.ai/docs" target="_blank"> | ||
AI SDK | ||
</a>{" "} | ||
in a{" "} | ||
<a href="https://nextjs.org/" target="_blank"> | ||
Next.js | ||
</a>{" "} | ||
project. | ||
</span> | ||
</li> | ||
<li className="hidden text-l md:block"> | ||
💻 | ||
<span className="ml-2"> | ||
You can find the prompt and model logic for this use-case in{" "} | ||
<code>app/api/chat/route.ts</code>. | ||
</span> | ||
</li> | ||
<li className="hidden text-l md:block"> | ||
🎨 | ||
<span className="ml-2"> | ||
The main frontend logic is found in <code>app/page.tsx</code>. | ||
</span> | ||
</li> | ||
<li className="text-l"> | ||
🐙 | ||
<span className="ml-2"> | ||
This template is open source - you can see the source code and | ||
deploy your own version{" "} | ||
<a | ||
href="https://github.com/michaelessiet/solana-agent-nextjs-starter-langchain" | ||
target="_blank" | ||
> | ||
from the GitHub repo | ||
</a> | ||
! | ||
</span> | ||
</li> | ||
<li className="text-l"> | ||
👇 | ||
<span className="ml-2"> | ||
Try asking e.g. <code>What is my wallet address?</code> below! | ||
</span> | ||
</li> | ||
</ul> | ||
</div> | ||
); | ||
return ( | ||
<ChatWindow | ||
endpoint="api/chat" | ||
emoji="🤖" | ||
titleText="Solana agent" | ||
placeholder="I'm your friendly Solana agent! Ask me anything..." | ||
emptyStateComponent={InfoCard} | ||
></ChatWindow> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import markdownToHtml from "@/utils/markdownToHTML"; | ||
import type { Message } from "ai/react"; | ||
import { useMemo } from "react"; | ||
|
||
export function ChatMessageBubble(props: { | ||
message: Message; | ||
aiEmoji?: string; | ||
sources: any[]; | ||
}) { | ||
const colorClassName = | ||
props.message.role === "user" ? "bg-sky-600" : "bg-slate-50 text-black"; | ||
const alignmentClassName = | ||
props.message.role === "user" ? "ml-auto" : "mr-auto"; | ||
const prefix = props.message.role === "user" ? "🧑" : props.aiEmoji; | ||
|
||
const content = useMemo(() => { | ||
return markdownToHtml(props.message.content); | ||
}, [props.message.content]); | ||
|
||
return ( | ||
<div | ||
className={`${alignmentClassName} ${colorClassName} rounded px-4 py-2 max-w-[80%] mb-8 flex`} | ||
> | ||
<div className="mr-2">{prefix}</div> | ||
<div className="flex flex-col"> | ||
<div | ||
className="prose prose-lg max-w-none" | ||
dangerouslySetInnerHTML={{ __html: content }} | ||
></div> | ||
{props.sources && props.sources.length ? ( | ||
<> | ||
<code className="mt-4 mr-auto bg-slate-600 px-2 py-1 rounded"> | ||
<h2>🔍 Sources:</h2> | ||
</code> | ||
<code className="mt-1 mr-2 bg-slate-600 px-2 py-1 rounded text-xs"> | ||
{props.sources?.map((source, i) => ( | ||
<div className="mt-2" key={"source:" + i}> | ||
{i + 1}. "{source.pageContent}" | ||
{source.metadata?.loc?.lines !== undefined ? ( | ||
<div> | ||
<br /> | ||
Lines {source.metadata?.loc?.lines?.from} to{" "} | ||
{source.metadata?.loc?.lines?.to} | ||
</div> | ||
) : ( | ||
"" | ||
)} | ||
</div> | ||
))} | ||
</code> | ||
</> | ||
) : ( | ||
"" | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.