From 10673f4d1368167db2cdf84ccf43fc1a40e91f64 Mon Sep 17 00:00:00 2001 From: Reihan Date: Mon, 17 Feb 2025 15:21:20 +0700 Subject: [PATCH] init --- .env | 9 + .gitignore | 39 + README.md | 40 + SCHEMA.sql | 8 + TODO | 28 + app/api/chat/route.js | 246 + components.json | 21 + components/chat.jsx | 91 + components/nav-user.jsx | 102 + components/sidebar-app.jsx | 182 + components/ui/avatar.jsx | 33 + components/ui/breadcrumb.jsx | 92 + components/ui/button.jsx | 48 + components/ui/card.jsx | 50 + components/ui/chat-input.jsx | 153 + components/ui/chat-message-area.jsx | 56 + components/ui/chat-message.jsx | 262 + components/ui/dialog.jsx | 94 + components/ui/dropdown-menu.jsx | 158 + components/ui/input.jsx | 19 + components/ui/label.jsx | 16 + components/ui/markdown-content.jsx | 339 + components/ui/radio-group.jsx | 29 + components/ui/scroll-area.jsx | 38 + components/ui/select.jsx | 119 + components/ui/separator.jsx | 23 + components/ui/sheet.jsx | 108 + components/ui/sidebar.jsx | 620 + components/ui/skeleton.jsx | 14 + components/ui/textarea.jsx | 18 + components/ui/tooltip.jsx | 28 + eslint.config.mjs | 14 + hooks/use-mobile.jsx | 19 + hooks/use-scroll-to-bottom.js | 111 + hooks/use-textarea-resize.js | 35 + jsconfig.json | 7 + lib/solana/PrivyEmbeddedWallet.js | 46 + lib/utils.js | 13 + next.config.mjs | 6 + package-lock.json | 14657 ++++++++++++++++ package.json | 60 + pages/_app.js | 41 + pages/_document.js | 13 + pages/api/test.js | 86 + pages/chat/[chatid].js | 121 + pages/chat/index.js | 66 + pages/index.js | 5 + pages/onboarding/delegate.js | 128 + pages/onboarding/index.js | 108 + pages/onboarding/login.js | 108 + postcss.config.mjs | 8 + public/favicon.ico | Bin 0 -> 25931 bytes server/checkpointToVercelAI.js | 147 + server/checkpointer.js | 3 + server/db.js | 7 + server/graph.js | 102 + server/tools/solana/transfer.js | 32 + server/tools/testing.js | 70 + solana-agent-kit/agent/index.js | 177 + solana-agent-kit/constants/index.js | 37 + solana-agent-kit/index.js | 7 + .../langchain/agent/create_image.js | 38 + solana-agent-kit/langchain/agent/index.js | 2 + .../langchain/agent/wallet_address.js | 15 + .../langchain/gibwork/create_task.js | 51 + solana-agent-kit/langchain/gibwork/index.js | 1 + .../langchain/helius/get_all_assets.js | 38 + solana-agent-kit/langchain/helius/index.js | 2 + .../langchain/helius/parse_transaction.js | 33 + solana-agent-kit/langchain/index.js | 51 + .../langchain/jupiter/fetch_price.js | 34 + solana-agent-kit/langchain/jupiter/index.js | 3 + solana-agent-kit/langchain/jupiter/stake.js | 35 + solana-agent-kit/langchain/jupiter/trade.js | 48 + solana-agent-kit/langchain/pumpfun/index.js | 1 + .../langchain/pumpfun/launch_pumpfun_token.js | 79 + solana-agent-kit/langchain/rugcheck/index.js | 2 + .../rugcheck/token_report_detailed.js | 32 + .../rugcheck/token_report_summary.js | 32 + solana-agent-kit/langchain/sns/index.js | 1 + .../langchain/sns/resolve_domain.js | 36 + solana-agent-kit/langchain/solana/balance.js | 37 + .../langchain/solana/balance_other.js | 44 + .../langchain/solana/close_empty_accounts.js | 33 + solana-agent-kit/langchain/solana/get_tps.js | 20 + solana-agent-kit/langchain/solana/index.js | 5 + solana-agent-kit/langchain/solana/transfer.js | 49 + solana-agent-kit/langchain/tiplink/index.js | 1 + solana-agent-kit/langchain/tiplink/tiplink.js | 50 + solana-agent-kit/tools/agent/create_image.js | 33 + .../tools/agent/get_wallet_address.js | 8 + solana-agent-kit/tools/agent/index.js | 2 + .../tools/gibwork/create_gibwork_task.js | 79 + solana-agent-kit/tools/gibwork/index.js | 1 + .../tools/helius/get_assets_by_owner.js | 50 + .../helius/helius_transaction_parsing.js | 39 + solana-agent-kit/tools/helius/index.js | 2 + solana-agent-kit/tools/index.js | 9 + solana-agent-kit/tools/jupiter/fetch_price.js | 26 + solana-agent-kit/tools/jupiter/index.js | 3 + .../tools/jupiter/stake_with_jup.js | 53 + solana-agent-kit/tools/jupiter/trade.js | 160 + solana-agent-kit/tools/pumpfun/index.js | 1 + .../tools/pumpfun/launch_pumpfun_token.js | 190 + solana-agent-kit/tools/rugcheck/index.js | 1 + solana-agent-kit/tools/rugcheck/rugcheck.js | 47 + solana-agent-kit/tools/sns/index.js | 1 + .../tools/sns/resolve_sol_domain.js | 26 + .../solana/close_empty_token_accounts.js | 93 + solana-agent-kit/tools/solana/get_balance.js | 21 + .../tools/solana/get_balance_other.js | 41 + solana-agent-kit/tools/solana/get_tps.js | 15 + solana-agent-kit/tools/solana/index.js | 5 + solana-agent-kit/tools/solana/transfer.js | 77 + .../tools/tiplink/create_tiplinks.js | 106 + solana-agent-kit/tools/tiplink/index.js | 1 + solana-agent-kit/utils/keypair.js | 32 + solana-agent-kit/utils/send_tx.js | 199 + solana-agent-kit/wallet/EmbeddedWallet.js | 59 + styles/globals.css | 102 + tailwind.config.mjs | 71 + test/checkpointer to vercel ai messages.js | 152 + test/graph.js | 98 + test/graph.png | Bin 0 -> 12810 bytes test/package-lock.json | 1418 ++ test/package.json | 21 + test/tools/testing.js | 70 + ...graph react agent with resume interrupt.js | 281 + tools/analyzeRepoTool/analyzeRepoTool.js | 123 + tools/birdeye/client.js | 28 + tools/birdeye/getCurrentPrices.js | 57 + tools/birdeye/getOHLCV.js | 165 + tools/birdeye/getPortfolio.js | 58 + tools/birdeye/getTokenOverview.js | 78 + tools/birdeye/getTrendingTokens.js | 53 + tools/birdeye/searchTokens.js | 80 + tools/rugcheck/rugcheckTool.js | 42 + tools/solscan/getTokenMetadata.js | 42 + tools/solscan/getTransactionDetails.js | 104 + tools/twitter/getTweet.js | 60 + tools/twitter/getTwitterProfile.js | 77 + tools/twitter/searchTweets.js | 71 + 142 files changed, 24721 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 README.md create mode 100644 SCHEMA.sql create mode 100644 TODO create mode 100644 app/api/chat/route.js create mode 100644 components.json create mode 100644 components/chat.jsx create mode 100644 components/nav-user.jsx create mode 100644 components/sidebar-app.jsx create mode 100644 components/ui/avatar.jsx create mode 100644 components/ui/breadcrumb.jsx create mode 100644 components/ui/button.jsx create mode 100644 components/ui/card.jsx create mode 100644 components/ui/chat-input.jsx create mode 100644 components/ui/chat-message-area.jsx create mode 100644 components/ui/chat-message.jsx create mode 100644 components/ui/dialog.jsx create mode 100644 components/ui/dropdown-menu.jsx create mode 100644 components/ui/input.jsx create mode 100644 components/ui/label.jsx create mode 100644 components/ui/markdown-content.jsx create mode 100644 components/ui/radio-group.jsx create mode 100644 components/ui/scroll-area.jsx create mode 100644 components/ui/select.jsx create mode 100644 components/ui/separator.jsx create mode 100644 components/ui/sheet.jsx create mode 100644 components/ui/sidebar.jsx create mode 100644 components/ui/skeleton.jsx create mode 100644 components/ui/textarea.jsx create mode 100644 components/ui/tooltip.jsx create mode 100644 eslint.config.mjs create mode 100644 hooks/use-mobile.jsx create mode 100644 hooks/use-scroll-to-bottom.js create mode 100644 hooks/use-textarea-resize.js create mode 100644 jsconfig.json create mode 100644 lib/solana/PrivyEmbeddedWallet.js create mode 100644 lib/utils.js create mode 100644 next.config.mjs create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pages/_app.js create mode 100644 pages/_document.js create mode 100644 pages/api/test.js create mode 100644 pages/chat/[chatid].js create mode 100644 pages/chat/index.js create mode 100644 pages/index.js create mode 100644 pages/onboarding/delegate.js create mode 100644 pages/onboarding/index.js create mode 100644 pages/onboarding/login.js create mode 100644 postcss.config.mjs create mode 100644 public/favicon.ico create mode 100644 server/checkpointToVercelAI.js create mode 100644 server/checkpointer.js create mode 100644 server/db.js create mode 100644 server/graph.js create mode 100644 server/tools/solana/transfer.js create mode 100644 server/tools/testing.js create mode 100644 solana-agent-kit/agent/index.js create mode 100644 solana-agent-kit/constants/index.js create mode 100644 solana-agent-kit/index.js create mode 100644 solana-agent-kit/langchain/agent/create_image.js create mode 100644 solana-agent-kit/langchain/agent/index.js create mode 100644 solana-agent-kit/langchain/agent/wallet_address.js create mode 100644 solana-agent-kit/langchain/gibwork/create_task.js create mode 100644 solana-agent-kit/langchain/gibwork/index.js create mode 100644 solana-agent-kit/langchain/helius/get_all_assets.js create mode 100644 solana-agent-kit/langchain/helius/index.js create mode 100644 solana-agent-kit/langchain/helius/parse_transaction.js create mode 100644 solana-agent-kit/langchain/index.js create mode 100644 solana-agent-kit/langchain/jupiter/fetch_price.js create mode 100644 solana-agent-kit/langchain/jupiter/index.js create mode 100644 solana-agent-kit/langchain/jupiter/stake.js create mode 100644 solana-agent-kit/langchain/jupiter/trade.js create mode 100644 solana-agent-kit/langchain/pumpfun/index.js create mode 100644 solana-agent-kit/langchain/pumpfun/launch_pumpfun_token.js create mode 100644 solana-agent-kit/langchain/rugcheck/index.js create mode 100644 solana-agent-kit/langchain/rugcheck/token_report_detailed.js create mode 100644 solana-agent-kit/langchain/rugcheck/token_report_summary.js create mode 100644 solana-agent-kit/langchain/sns/index.js create mode 100644 solana-agent-kit/langchain/sns/resolve_domain.js create mode 100644 solana-agent-kit/langchain/solana/balance.js create mode 100644 solana-agent-kit/langchain/solana/balance_other.js create mode 100644 solana-agent-kit/langchain/solana/close_empty_accounts.js create mode 100644 solana-agent-kit/langchain/solana/get_tps.js create mode 100644 solana-agent-kit/langchain/solana/index.js create mode 100644 solana-agent-kit/langchain/solana/transfer.js create mode 100644 solana-agent-kit/langchain/tiplink/index.js create mode 100644 solana-agent-kit/langchain/tiplink/tiplink.js create mode 100644 solana-agent-kit/tools/agent/create_image.js create mode 100644 solana-agent-kit/tools/agent/get_wallet_address.js create mode 100644 solana-agent-kit/tools/agent/index.js create mode 100644 solana-agent-kit/tools/gibwork/create_gibwork_task.js create mode 100644 solana-agent-kit/tools/gibwork/index.js create mode 100644 solana-agent-kit/tools/helius/get_assets_by_owner.js create mode 100644 solana-agent-kit/tools/helius/helius_transaction_parsing.js create mode 100644 solana-agent-kit/tools/helius/index.js create mode 100644 solana-agent-kit/tools/index.js create mode 100644 solana-agent-kit/tools/jupiter/fetch_price.js create mode 100644 solana-agent-kit/tools/jupiter/index.js create mode 100644 solana-agent-kit/tools/jupiter/stake_with_jup.js create mode 100644 solana-agent-kit/tools/jupiter/trade.js create mode 100644 solana-agent-kit/tools/pumpfun/index.js create mode 100644 solana-agent-kit/tools/pumpfun/launch_pumpfun_token.js create mode 100644 solana-agent-kit/tools/rugcheck/index.js create mode 100644 solana-agent-kit/tools/rugcheck/rugcheck.js create mode 100644 solana-agent-kit/tools/sns/index.js create mode 100644 solana-agent-kit/tools/sns/resolve_sol_domain.js create mode 100644 solana-agent-kit/tools/solana/close_empty_token_accounts.js create mode 100644 solana-agent-kit/tools/solana/get_balance.js create mode 100644 solana-agent-kit/tools/solana/get_balance_other.js create mode 100644 solana-agent-kit/tools/solana/get_tps.js create mode 100644 solana-agent-kit/tools/solana/index.js create mode 100644 solana-agent-kit/tools/solana/transfer.js create mode 100644 solana-agent-kit/tools/tiplink/create_tiplinks.js create mode 100644 solana-agent-kit/tools/tiplink/index.js create mode 100644 solana-agent-kit/utils/keypair.js create mode 100644 solana-agent-kit/utils/send_tx.js create mode 100644 solana-agent-kit/wallet/EmbeddedWallet.js create mode 100644 styles/globals.css create mode 100644 tailwind.config.mjs create mode 100644 test/checkpointer to vercel ai messages.js create mode 100644 test/graph.js create mode 100644 test/graph.png create mode 100644 test/package-lock.json create mode 100644 test/package.json create mode 100644 test/tools/testing.js create mode 100644 test/working langgraph react agent with resume interrupt.js create mode 100644 tools/analyzeRepoTool/analyzeRepoTool.js create mode 100644 tools/birdeye/client.js create mode 100644 tools/birdeye/getCurrentPrices.js create mode 100644 tools/birdeye/getOHLCV.js create mode 100644 tools/birdeye/getPortfolio.js create mode 100644 tools/birdeye/getTokenOverview.js create mode 100644 tools/birdeye/getTrendingTokens.js create mode 100644 tools/birdeye/searchTokens.js create mode 100644 tools/rugcheck/rugcheckTool.js create mode 100644 tools/solscan/getTokenMetadata.js create mode 100644 tools/solscan/getTransactionDetails.js create mode 100644 tools/twitter/getTweet.js create mode 100644 tools/twitter/getTwitterProfile.js create mode 100644 tools/twitter/searchTweets.js diff --git a/.env b/.env new file mode 100644 index 0000000..1fce58b --- /dev/null +++ b/.env @@ -0,0 +1,9 @@ +POSTGRES_DB_URL=postgres://postgres:eMd9hliASrN1yuNOYSk7LtOdlLRnnlnhUF31JKww6zQ=@supabase-pgdb-1:5432/solana +NEXT_PUBLIC_PRIVY_APP_ID=cm6nnbni501avu3k6sfxrc8ts +PRIVY_APP_SECRET=2j5KefComuF3Fm3YbDPh2udnSjfi2uYrSihG7PmXij7JDC45sobRGCoYLSYS21sRyW3De4SbFmw9mtTiyqNtoHub +PRIVY_WALLET_SIGNING_KEY=wallet-auth:MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgDJtG6Ejxyshi9/g1Qb/qWaWpRNEoMcDnF1MhE8r/3SWhRANCAAQl03/3auCjNZm1GHjdpAyw73ZoMlAAVfnXtVlDRSJCheupk6A0PQNdy/NL7mOfspiLxKnvq/mfurI2HGfthVDI +OPENAI_API_KEY="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" +SOLSCAN_API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjcmVhdGVkQXQiOjE3MzAwNDg4Njc1ODUsImVtYWlsIjoic2hpYWxvdGhAZ21haWwuY29tIiwiYWN0aW9uIjoidG9rZW4tYXBpIiwiYXBpVmVyc2lvbiI6InYyIiwiaWF0IjoxNzMwMDQ4ODY3fQ.JKKAyBvlfB68zMidRipBfXv2l-a_eEIA3gAU5wxusyQ +BIRDEYE_API_KEY=26e4687aad6f4630a3cd76f85a147657 +RPC_URL=https://mainnet.helius-rpc.com/?api-key=22e4b61a-a7f2-47ab-bbc6-48d095a9ceb4 +HELIUS_API_KEY=22e4b61a-a7f2-47ab-bbc6-48d095a9ceb4 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50b2309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb8aec4 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/pages/api-reference/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) instead of React pages. + +This project uses [`next/font`](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn-pages-router) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/pages/building-your-application/deploying) for more details. diff --git a/SCHEMA.sql b/SCHEMA.sql new file mode 100644 index 0000000..0e4393e --- /dev/null +++ b/SCHEMA.sql @@ -0,0 +1,8 @@ +CREATE TABLE users ( + userid TEXT PRIMARY KEY, + publickey TEXT NOT NULL, + last_signedin_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + tg_userid TEXT DEFAULT NULL, + delegated BOOLEAN DEFAULT FALSE +); diff --git a/TODO b/TODO new file mode 100644 index 0000000..541a526 --- /dev/null +++ b/TODO @@ -0,0 +1,28 @@ +- show user wallet +- show assets +- add export walet hook +- add table to store chat ids for user + - user id + - chat id + - created at + - title +- create chat title ai function +- trim messages from ai node +- clean up code + +- transfer function: + - to: address or domain name + - mint: default native sol + - add clear errors sand artifacts + +- trade function: + - automatically check which platform if pf or jup + - from + - to + - usd value buy in + - token buy in + - slippage + + +- implement image upload for pump fun creation. local minio s3 +- create pf token \ No newline at end of file diff --git a/app/api/chat/route.js b/app/api/chat/route.js new file mode 100644 index 0000000..88c96cb --- /dev/null +++ b/app/api/chat/route.js @@ -0,0 +1,246 @@ +import { NextResponse } from "next/server"; +import { ulid } from "ulid"; +import { Command } from "@langchain/langgraph"; +import { isAIMessage } from "@langchain/core/messages"; +import checkpointer from "@/server/checkpointer"; +import { PrivyClient } from '@privy-io/server-auth'; +import { PublicKey } from '@solana/web3.js'; +import { SolanaAgentKit } from '@/solana-agent-kit'; +import pool from "@/server/db"; +import { createPrivyEmbeddedWallet } from '@/lib/solana/PrivyEmbeddedWallet'; +import getGraph from "@/server/graph"; + +export const dynamic = "force-dynamic"; + +const PRIVY_APP_ID = process.env.NEXT_PUBLIC_PRIVY_APP_ID; +const PRIVY_APP_SECRET = process.env.PRIVY_APP_SECRET; +const PRIVY_WALLET_SIGNING_KEY = process.env.PRIVY_WALLET_SIGNING_KEY; +const RPC_URL = process.env.RPC_URL; +const OPENAI_API_KEY = process.env.OPENAI_API_KEY; +const HELIUS_API_KEY = process.env.HELIUS_API_KEY; + +// TODO +const SYSTEMprompt = `You are a senior Solana software engineer and market data expert. You have access to Solana Defi information. You could also analyze github repos and tweet profiles and posts and search twitter. +Your goal is to give you clean and concise responses to users to help them on your Solana journey. You must analyze carefully the user input, then reason about the possible routes you could take using the tools available to you, dont return information not asked for by the user. All tool calls are visible to the user so DO NOT repeat what is said in the tools. +YOU MUST draw your own conclusions and analysis from the tool responses. Only your analysis must be show the user. Keep it concise and small. If user asks for list of tokens trending the charts will be showm, DO NOT LIST RETURN THEM TO THE USER! KEEP RESPONSES SHORT!`; + +export async function POST(req) { + try { + const body = await req.json(); + console.log(body); + + const { message, thread_id, approveObj = {} } = body; + console.log(thread_id); + + if (!thread_id) { + return NextResponse.json({ error: "Unauthorized. Please sign in" }, { status: 401 }); + } + if (!message && approveObj == {}) { + return NextResponse.json({ error: "Empty message" }, { status: 401 }); + } + + + const cookieAuthToken = req.cookies.get("privy-id-token"); + console.log(cookieAuthToken); + + if (!cookieAuthToken) { + console.log("No authentication token found."); + return NextResponse.json({ error: "Unauthorized: No auth token" }, { status: 401 }); + } + + const PRIVY_SERVER_CLIENT = new PrivyClient(PRIVY_APP_ID, PRIVY_APP_SECRET, { + walletApi: { + authorizationPrivateKey: PRIVY_WALLET_SIGNING_KEY, + }, + }); + + console.log("Verifying auth token..."); + const claims = await PRIVY_SERVER_CLIENT.verifyAuthToken(cookieAuthToken.value); + const userId = claims.userId; + console.log("Authenticated user ID:", userId); + + const dbRes = await pool.query("SELECT userid, publickey, tg_userid, delegated FROM users WHERE userid = $1", [userId]); + if (dbRes.rows.length === 1) { + const { userid, publickey } = dbRes.rows[0]; + console.log("User found in database:", userid, "with public key:", publickey); + + const walletAdapter = new createPrivyEmbeddedWallet( + PRIVY_SERVER_CLIENT, + new PublicKey(publickey) + ); + + const solAgentKit = new SolanaAgentKit(walletAdapter, RPC_URL, { + OPENAI_API_KEY, + HELIUS_API_KEY + }); + const graph = getGraph(solAgentKit) + + const input = { + role: "user", + content: message ?? "", + }; + + const config = { configurable: { thread_id } }; + let passToGraph = { messages: [input] }; + + const checkpointerData = await checkpointer.get(config); + + if ( + typeof checkpointerData?.channel_values === "object" && + "branch:agent:condition:checkApproval" in checkpointerData.channel_values + ) { + console.log("=".repeat(75)); + console.log(`${"=".repeat(25)} Graph interrupted for user input ${"=".repeat(25)}`); + if (Object.keys(approveObj).length == 0) { + return NextResponse.json({ error: "Please accept or reject the tools" }, { status: 401 }); + } + passToGraph = new Command({ resume: approveObj }); + } + + const transformStream = new ReadableStream({ + async start(controller) { + const textEncoder = new TextEncoder(); + try { + for await (const event of await graph.stream(passToGraph, config)) { + if (event.__interrupt__) { + console.log("=".repeat(75)); + console.log("=" + "=".repeat(25) + " Interrupted for permission " + "=".repeat(25)); + console.log(JSON.stringify(event.__interrupt__)); + console.log("=".repeat(75)); + const toolCalls = event.__interrupt__[0].value.map((tool) => ({ + id: tool.id, + name: tool.name, + args: Object.fromEntries(Object.entries(tool.args).filter(([key]) => key !== "debug")), + })); + console.log(toolCalls); + + controller.enqueue(`f:{"messageId":"${event.__interrupt__[0].ns[0]}"}\n`); + const interrupter_tool_id = ulid(); + controller.enqueue( + `9:${JSON.stringify({ + toolCallId: interrupter_tool_id, + toolName: "interrupter", + args: { toolCalls }, + })}\n`, + ); + + controller.enqueue( + `a:${JSON.stringify({ + toolCallId: interrupter_tool_id, + result: "Your input is required to proceed", + })}\n`, + ); + + controller.enqueue( + `e:${JSON.stringify({ + finishReason: "tool-calls", + usage: { promptTokens: 0, completionTokens: 0 }, + isContinued: false, + })}\n`, + ); + } else if (event.checkApproval) { + continue; + } else if (event.agent) { + console.log("=".repeat(75)); + console.log("=" + "=".repeat(25) + " Agent node " + "=".repeat(25)); + const message = event.agent.messages[0]; + console.log(message); + + if (message.content) { + controller.enqueue(`f:{"messageId":"${message.id}"}\n`); + controller.enqueue(`0:${JSON.stringify(message.content)}\n`); + controller.enqueue( + `e:${JSON.stringify({ + finishReason: "stop", + usage: { + promptTokens: message.usage_metadata.input_tokens, + completionTokens: message.usage_metadata.output_tokens, + }, + isContinued: false, + })}\n`, + ); + } + } else if (event.allToolsNode) { + console.log("=".repeat(75)); + console.log("=" + "=".repeat(25) + " All tools node " + "=".repeat(25)); + console.log(event.allToolsNode); + console.log("=".repeat(75)); + for (const toolMessage of event.allToolsNode.messages) { + controller.enqueue(`f:{"messageId":"${toolMessage.id}"}\n`); + + controller.enqueue( + textEncoder.encode( + `9:${JSON.stringify({ + toolCallId: toolMessage.tool_call_id, + toolName: toolMessage.name, + args: { artifact: toolMessage.artifact }, + })}\n`, + ), + ); + + controller.enqueue( + textEncoder.encode( + `a:${JSON.stringify({ + toolCallId: toolMessage.tool_call_id, + result: toolMessage.content, + })}\n`, + ), + ); + + controller.enqueue( + `e:${JSON.stringify({ + finishReason: "tool-calls", + usage: { promptTokens: 0, completionTokens: 0 }, + isContinued: false, + })}\n`, + ); + } + } + } + const checkpointerData = await checkpointer.get(config); + + if ( + typeof checkpointerData?.channel_values === "object" && + !("branch:agent:condition:checkApproval" in checkpointerData.channel_values) + ) { + const lastMessage = checkpointerData?.channel_values?.messages?.at(-1); + const lastMessage_hasToolCalls = lastMessage?.tool_calls?.length > 0; + const lastMessage_isAIMessageFlag = lastMessage ? isAIMessage(lastMessage) : false; + + if (lastMessage_isAIMessageFlag && !lastMessage_hasToolCalls) { + console.log("Breaking loop: Last message is an AI message with no tool calls."); + // console.log(checkpointerData); + controller.enqueue(`d:{"finishReason":"stop","usage":{"promptTokens":0,"completionTokens":0}}\n`); + } + } + } catch (error) { + controller.enqueue(`3:${JSON.stringify(error.message)}\n`); + controller.error(error); + } finally { + controller.close(); + } + }, + }); + + const response = new Response(transformStream, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + "x-vercel-ai-data-stream": "v1", + }, + }); + + return response; + + + + } else { + console.log("User not found in database."); + return NextResponse.json({ error: "User not found" }, { status: 404 }); + } + } catch (e) { + console.log(e); + return NextResponse.json({ error: "Error while processing your request. Please try again later" }, { status: 500 }); + } +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..d76325a --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": false, + "tailwind": { + "config": "tailwind.config.mjs", + "css": "styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/components/chat.jsx b/components/chat.jsx new file mode 100644 index 0000000..481bdbd --- /dev/null +++ b/components/chat.jsx @@ -0,0 +1,91 @@ +"use client" +import { + ChatInput, + ChatInputSubmit, + ChatInputTextArea +} from "@/components/ui/chat-input" +import { + ChatMessage, + ChatMessageAvatar, + ChatMessageContent +} from "@/components/ui/chat-message" +import { ChatMessageArea } from "@/components/ui/chat-message-area" +import { useChat } from "ai/react" + +export function Chat({ className,thread_id,initialMessages, ...props }) { + const { + messages, + input, + handleInputChange, + handleSubmit, + isLoading, + stop + } = useChat({ + id: thread_id, + api: "/api/chat", + sendExtraMessageFields: true, + experimental_prepareRequestBody({ messages, id, requestData }) { + console.log(requestData); + + return { message: messages[messages.length - 1].content, thread_id, approveObj:requestData }; + }, + initialMessages: initialMessages, + onFinish: message => { + console.log("onFinish", message, completion); + }, + onToolCall: tool => console.log(tool) + }) + console.log("messages",messages); + + const handleSubmitMessage = () => { + if (isLoading) { + return + } + handleSubmit() + } + + return ( +
+ +
+ {messages.map(message => { + + + if (message.role !== "user") { + return ( + + + + + ) + } + if (!message.content) return null; + + return ( + + + + ) + })} +
+
+
+ + + + +
+
+ ) +} diff --git a/components/nav-user.jsx b/components/nav-user.jsx new file mode 100644 index 0000000..6c2c42f --- /dev/null +++ b/components/nav-user.jsx @@ -0,0 +1,102 @@ +"use client" + +import { + BadgeCheck, + Bell, + ChevronsUpDown, + CreditCard, + LogOut, + Sparkles +} from "lucide-react" + +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu" +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar +} from "@/components/ui/sidebar" + +export function NavUser({ user }) { + const { isMobile } = useSidebar() + + return ( + + + + + + + + CN + +
+ {user.name} + {user.email} +
+ +
+
+ + +
+ + + CN + +
+ {user.name} + {user.email} +
+
+
+ + + + + Upgrade to Pro + + + + + + + Account + + + + Billing + + + + Notifications + + + + + + Log out + +
+
+
+
+ ) +} diff --git a/components/sidebar-app.jsx b/components/sidebar-app.jsx new file mode 100644 index 0000000..08a6663 --- /dev/null +++ b/components/sidebar-app.jsx @@ -0,0 +1,182 @@ +"use client" +import { Button } from "@/components/ui/button" +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarGroup, + SidebarGroupLabel, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarRail +} from "@/components/ui/sidebar" +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "@/components/ui/tooltip" +import { NavUser } from "@/components/nav-user" +import { MessageCircle, SquarePen } from "lucide-react" + +// This is sample data. +const data = { + user: { + name: "John Doe", + email: "m@example.com", + avatar: "/avatar-1.png" + }, + recentChats: [ + { + title: "Project Planning Assistant", + date: new Date(2024, 2, 20), + url: "#" + }, + { + title: "Code Review Helper", + date: new Date(2024, 2, 19), + url: "#" + }, + { + title: "Bug Analysis Chat", + date: new Date(2024, 2, 18), + url: "#" + } + ], + lastWeekChats: [ + { + title: "API Design Discussion", + date: new Date(2024, 2, 15), + url: "#" + }, + { + title: "Database Schema Planning", + date: new Date(2024, 2, 14), + url: "#" + } + ], + lastMonthChats: [ + { + title: "Architecture Overview", + date: new Date(2024, 1, 28), + url: "#" + }, + { + title: "Performance Optimization", + date: new Date(2024, 1, 25), + url: "#" + } + ], + previousChats: [ + { + title: "Initial Project Setup", + date: new Date(2023, 11, 15), + url: "#" + }, + { + title: "Requirements Analysis", + date: new Date(2023, 11, 10), + url: "#" + } + ] +} + +export function SidebarApp({ ...props }) { + return ( + + +
+
+
+ +
+ simple-ai +
+ {/* New Chat Button */} + + + + + + +

New Chat

+
+
+
+
+
+ +
+ {/* Recent Chats */} + + Recent + + {data.recentChats.map(chat => ( + + + + {chat.title} + + + ))} + + + + {/* Previous 7 Days */} + + Previous 7 Days + + {data.lastWeekChats.map(chat => ( + + + + {chat.title} + + + ))} + + + + {/* Previous 30 Days */} + + Previous 30 Days + + {data.lastMonthChats.map(chat => ( + + + + {chat.title} + + + ))} + + + + {/* Previous Years */} + + Previous Years + + {data.previousChats.map(chat => ( + + + + {chat.title} + + + ))} + + +
+
+ + + + +
+ ) +} diff --git a/components/ui/avatar.jsx b/components/ui/avatar.jsx new file mode 100644 index 0000000..9a2f853 --- /dev/null +++ b/components/ui/avatar.jsx @@ -0,0 +1,33 @@ +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/components/ui/breadcrumb.jsx b/components/ui/breadcrumb.jsx new file mode 100644 index 0000000..4d782a4 --- /dev/null +++ b/components/ui/breadcrumb.jsx @@ -0,0 +1,92 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef( + ({ ...props }, ref) =>