From 0ec616dd838b8843b7ca75cd28835aa8cabc6b68 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 23 Jan 2025 15:41:59 +0530 Subject: [PATCH 1/7] switched from brian AI to Openai --- client/app/api/transactions/route.ts | 334 +++++++++++++-------------- client/prompts/prompts.js | 29 +++ 2 files changed, 192 insertions(+), 171 deletions(-) diff --git a/client/app/api/transactions/route.ts b/client/app/api/transactions/route.ts index c1cbc14a..5ec6959b 100644 --- a/client/app/api/transactions/route.ts +++ b/client/app/api/transactions/route.ts @@ -1,218 +1,210 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { NextRequest, NextResponse } from 'next/server'; -import { transactionProcessor } from '@/lib/transaction'; -import { LayerswapClient } from '@/lib/layerswap/client'; -import type { BrianResponse, BrianTransactionData } from '@/lib/transaction/types'; - -const BRIAN_API_URL = 'https://api.brianknows.org/api/v0/agent'; -const layerswapClient = new LayerswapClient(process.env.LAYERSWAP_API_KEY || ''); - -async function convertBrianResponseFormat(apiResponse: any): Promise { - const response = apiResponse.result[0]; - - // Construct base response - const brianResponse: BrianResponse = { - solver: response.solver, - action: response.action, - type: response.type, - extractedParams: response.extractedParams, - data: {} as BrianTransactionData - }; - - // Convert data based on action type - switch (response.action) { - case 'swap': - case 'transfer': - brianResponse.data = { - description: response.data?.description || '', - steps: response.data?.steps?.map((step: any) => ({ - contractAddress: step.contractAddress, - entrypoint: step.entrypoint, - calldata: step.calldata - })) || [], - fromToken: response.data?.fromToken, - toToken: response.data?.toToken, - fromAmount: response.data?.fromAmount, - toAmount: response.data?.toAmount, - receiver: response.data?.receiver, - amountToApprove: response.data?.amountToApprove, - gasCostUSD: response.data?.gasCostUSD - }; - break; - - case 'bridge': - brianResponse.data = { - description: '', - steps: [], - bridge: { - sourceNetwork: response.extractedParams.chain, - destinationNetwork: response.extractedParams.dest_chain, - sourceToken: response.extractedParams.token1, - destinationToken: response.extractedParams.token2, - amount: parseFloat(response.extractedParams.amount), - sourceAddress: response.extractedParams.address || '', - destinationAddress: response.extractedParams.address || '' - } - }; - break; - - case 'deposit': - case 'withdraw': - brianResponse.data = { - description: '', - steps: [], - protocol: response.extractedParams.protocol, - fromAmount: response.extractedParams.amount, - toAmount: response.extractedParams.amount, - receiver: response.extractedParams.address || '' - }; - break; - - default: - throw new Error(`Unsupported action type: ${response.action}`); - } - - return brianResponse; -} - -async function getBrianTransactionData(prompt: string, address: string, chainId: string, messages: any[]): Promise { +import { NextResponse, NextRequest } from "next/server"; +import OpenAI from "openai"; +import { transactionProcessor } from "@/lib/transaction"; +import type { + BrianResponse, + BrianTransactionData, +} from "@/lib/transaction/types"; +import { TRANSACTION_INTENT_PROMPT } from "@/prompts/prompts"; + +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY || "", +}); + +async function getTransactionIntentFromOpenAI( + prompt: string, + address: string, + chainId: string, + messages: any[] +): Promise { try { - const response = await fetch(`${BRIAN_API_URL}/transaction`, { - method: 'POST', - headers: { - 'X-Brian-Api-Key': process.env.BRIAN_API_KEY || '', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - prompt, - address, - chainId: chainId.toString(), - messages - }), + const response = await openai.chat.completions.create({ + model: "gpt-4o-mini", + response_format: { type: "json_object" }, + messages: [ + { + role: "system", + content: `${TRANSACTION_INTENT_PROMPT}\n\nAdditional Context:\nCurrent Chain ID: ${chainId}`, + }, + { role: "user", content: prompt }, + ...messages.map((msg) => ({ + role: msg.role, + content: msg.content, + })), + ], }); - const data = await response.json(); - console.log('Brian API Response:', JSON.stringify(data, null, 2)); - - // Special handling for intent recognition errors that still have extracted params - if (!response.ok && data.error && data.extractedParams) { - // If we have extracted params, we can still proceed - if (data.extractedParams[0]) { - // Convert to expected format - return { - solver: "Brian-Starknet", - action: data.extractedParams[0].action as 'bridge', - type: "write", - extractedParams: data.extractedParams[0], - data: { - description: '', - steps: [], - bridge: { - sourceNetwork: data.extractedParams[0].chain, - destinationNetwork: data.extractedParams[0].dest_chain, - sourceToken: data.extractedParams[0].token1, - destinationToken: data.extractedParams[0].token2, - amount: parseFloat(data.extractedParams[0].amount), - sourceAddress: address, - destinationAddress: data.extractedParams[0].address - } - } - }; - } - } + const intentData = JSON.parse(response.choices[0].message.content || "{}"); - if (!response.ok) { - throw new Error(data.error || `API request failed with status ${response.status}`); + if (!intentData.isTransactionIntent) { + throw new Error("Not a transaction-related prompt"); } - if (!data.result?.[0]) { - throw new Error('Invalid response format from Brian API'); - } - - const brianResponse = data.result[0] as BrianResponse; + const brianResponse: BrianResponse = { + solver: intentData.solver || "OpenAI-Intent-Recognizer", + action: intentData.action, + type: "write", + extractedParams: { + action: intentData.extractedParams.action, + token1: intentData.extractedParams.token1 || "", + token2: intentData.extractedParams.token2 || "", + chain: intentData.extractedParams.chain || "", + dest_chain: intentData.extractedParams.dest_chain || "", + amount: intentData.extractedParams.amount || "", + protocol: intentData.extractedParams.protocol || "", + address: intentData.extractedParams.address || address, + destinationAddress: + intentData.extractedParams.destinationAddress || address, + destinationChain: intentData.extractedParams.dest_chain || "", + }, + data: {} as BrianTransactionData, + }; + + switch (brianResponse.action) { + case "swap": + case "transfer": + brianResponse.data = { + description: "", + steps: [], + fromToken: { + symbol: brianResponse.extractedParams?.token1 || "", + address: brianResponse.extractedParams?.address || "", + decimals: 1, // default adjust if needed + }, + toToken: { + symbol: brianResponse.extractedParams?.token2 || "", + address: brianResponse.extractedParams?.address || "", + decimals: 1, // default adjust if needed + }, + fromAmount: brianResponse.extractedParams?.amount, + toAmount: brianResponse.extractedParams?.amount, + receiver: brianResponse.extractedParams?.address, + }; + break; + + case "bridge": + brianResponse.data = { + description: "", + steps: [], + bridge: { + sourceNetwork: brianResponse.extractedParams?.chain || "", + destinationNetwork: brianResponse.extractedParams + ?.dest_chain as string, + sourceToken: brianResponse.extractedParams?.token1 || "", + destinationToken: brianResponse.extractedParams?.token2 || "", + amount: parseFloat(brianResponse.extractedParams?.amount || "0"), + sourceAddress: address, + destinationAddress: + brianResponse.extractedParams?.destinationAddress || "", + }, + }; + break; + + case "deposit": + case "withdraw": + brianResponse.data = { + description: "", + steps: [], + protocol: brianResponse.extractedParams?.protocol, + fromAmount: brianResponse.extractedParams?.amount, + toAmount: brianResponse.extractedParams?.amount, + receiver: brianResponse.extractedParams?.address, + }; + break; - // Add connected wallet address to params - if (brianResponse.extractedParams) { - brianResponse.extractedParams.connectedAddress = address; + default: + throw new Error(`Unsupported action type: ${brianResponse.action}`); } return brianResponse; } catch (error) { - console.error('Error fetching transaction data:', error); + console.error("Error fetching transaction intent:", error); throw error; } } - export async function POST(request: NextRequest) { try { const body = await request.json(); - const { prompt, address, messages = [], chainId = '4012' } = body; + const { prompt, address, messages = [], chainId = "4012" } = body; if (!prompt || !address) { return NextResponse.json( - { error: 'Missing required parameters (prompt or address)' }, + { error: "Missing required parameters (prompt or address)" }, { status: 400 } ); } try { - const brianResponse = await getBrianTransactionData(prompt, address, chainId, messages); - console.log('Processed Brian Response:', JSON.stringify(brianResponse, null, 2)); - - // Always pass connected address to handlers - if (brianResponse.extractedParams) { - brianResponse.extractedParams.connectedAddress = address; - } - - const processedTx = await transactionProcessor.processTransaction(brianResponse); - console.log('Processed Transaction:', JSON.stringify(processedTx, null, 2)); + const transactionIntent = await getTransactionIntentFromOpenAI( + prompt, + address, + chainId, + messages + ); + console.log( + "Processed Transaction Intent from OPENAI:", + JSON.stringify(transactionIntent, null, 2) + ); - // For deposit and withdraw, always use connected address as receiver - if (['deposit', 'withdraw'].includes(brianResponse.action)) { + const processedTx = await transactionProcessor.processTransaction( + transactionIntent + ); + console.log( + "Processed Transaction:", + JSON.stringify(processedTx, null, 2) + ); + + if (["deposit", "withdraw"].includes(transactionIntent.action)) { processedTx.receiver = address; } return NextResponse.json({ - result: [{ - data: { - description: processedTx.description, - transaction: { - type: processedTx.action, - data: { - transactions: processedTx.transactions, - fromToken: processedTx.fromToken, - toToken: processedTx.toToken, - fromAmount: processedTx.fromAmount, - toAmount: processedTx.toAmount, - receiver: processedTx.receiver, - gasCostUSD: processedTx.estimatedGas, - solver: processedTx.solver, - protocol: processedTx.protocol, - bridge: processedTx.bridge - } - } + result: [ + { + data: { + description: processedTx.description, + transaction: { + type: processedTx.action, + data: { + transactions: processedTx.transactions, + fromToken: processedTx.fromToken, + toToken: processedTx.toToken, + fromAmount: processedTx.fromAmount, + toAmount: processedTx.toAmount, + receiver: processedTx.receiver, + gasCostUSD: processedTx.estimatedGas, + solver: processedTx.solver, + protocol: processedTx.protocol, + bridge: processedTx.bridge, + }, + }, + }, + conversationHistory: messages, }, - conversationHistory: messages - }] + ], }); } catch (error) { - console.error('Transaction processing error:', error); + console.error("Transaction processing error:", error); return NextResponse.json( - { - error: error instanceof Error ? error.message : 'Transaction processing failed', - details: error instanceof Error ? error.stack : undefined + { + error: + error instanceof Error + ? error.message + : "Transaction processing failed", + details: error instanceof Error ? error.stack : undefined, }, { status: 400 } ); } } catch (error) { - console.error('Request processing error:', error); + console.error("Request processing error:", error); return NextResponse.json( - { error: 'Internal server error' }, + { error: "Internal server error" }, { status: 500 } ); } -} \ No newline at end of file +} diff --git a/client/prompts/prompts.js b/client/prompts/prompts.js index ba3d2bcd..2805ac96 100644 --- a/client/prompts/prompts.js +++ b/client/prompts/prompts.js @@ -13,3 +13,32 @@ Your responsibilities: 5. Format all responses in Markdown for better readability on the website. NOTE: On the website, always refer to yourself as "StarkFinder." Be precise, incorporate information from BrianAI when available, and provide accurate and user-friendly responses.`; + +export const TRANSACTION_INTENT_PROMPT = ` +You are a blockchain transaction intent recognition system. +Given a user prompt, analyze and determine if the request involves a blockchain transaction. + +Respond ONLY in JSON format with the following structure: +{ + "isTransactionIntent": boolean, + "solver": string, + "action": "swap" | "transfer" | "deposit" | "withdraw" | "bridge", + "type": "write", + "extractedParams": { + "action": string, + "token1": string, + "token2": string, + "chain": string, + "dest_chain": string, + "amount": string, + "protocol": string, + "address": string, + "destinationAddress": string + } +} + +Rules: +- If the prompt is NOT a transaction-related request, set isTransactionIntent to false +- Be precise in extracting transaction-specific parameters +- Use empty strings for parameters that cannot be determined +`; From 4bff1f424203b1615a66a241e460dde7e0398ac3 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 23 Jan 2025 19:43:50 +0530 Subject: [PATCH 2/7] made changes to the code --- client/app/api/transactions/route.ts | 114 +++++++++++---------------- 1 file changed, 47 insertions(+), 67 deletions(-) diff --git a/client/app/api/transactions/route.ts b/client/app/api/transactions/route.ts index 5ec6959b..bb65acf2 100644 --- a/client/app/api/transactions/route.ts +++ b/client/app/api/transactions/route.ts @@ -1,13 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ - import { NextResponse, NextRequest } from "next/server"; import OpenAI from "openai"; import { transactionProcessor } from "@/lib/transaction"; -import type { - BrianResponse, - BrianTransactionData, -} from "@/lib/transaction/types"; import { TRANSACTION_INTENT_PROMPT } from "@/prompts/prompts"; const openai = new OpenAI({ @@ -19,7 +14,7 @@ async function getTransactionIntentFromOpenAI( address: string, chainId: string, messages: any[] -): Promise { +): Promise { try { const response = await openai.chat.completions.create({ model: "gpt-4o-mini", @@ -43,7 +38,7 @@ async function getTransactionIntentFromOpenAI( throw new Error("Not a transaction-related prompt"); } - const brianResponse: BrianResponse = { + const transactionIntent = { solver: intentData.solver || "OpenAI-Intent-Recognizer", action: intentData.action, type: "write", @@ -52,74 +47,59 @@ async function getTransactionIntentFromOpenAI( token1: intentData.extractedParams.token1 || "", token2: intentData.extractedParams.token2 || "", chain: intentData.extractedParams.chain || "", - dest_chain: intentData.extractedParams.dest_chain || "", amount: intentData.extractedParams.amount || "", protocol: intentData.extractedParams.protocol || "", address: intentData.extractedParams.address || address, + dest_chain: intentData.extractedParams.dest_chain || "", destinationAddress: intentData.extractedParams.destinationAddress || address, - destinationChain: intentData.extractedParams.dest_chain || "", }, - data: {} as BrianTransactionData, + data: { + description: "", + steps: [], + ...(["swap", "transfer"].includes(intentData.action) + ? { + fromToken: { + symbol: intentData.extractedParams.token1 || "", + address: intentData.extractedParams.address || "", + decimals: 1, // default, adjust if needed + }, + toToken: { + symbol: intentData.extractedParams.token2 || "", + address: intentData.extractedParams.address || "", + decimals: 1, // default, adjust if needed + }, + fromAmount: intentData.extractedParams.amount, + toAmount: intentData.extractedParams.amount, + receiver: intentData.extractedParams.address, + } + : {}), + ...(intentData.action === "bridge" + ? { + bridge: { + sourceNetwork: intentData.extractedParams.chain || "", + destinationNetwork: intentData.extractedParams.dest_chain || "", + sourceToken: intentData.extractedParams.token1 || "", + destinationToken: intentData.extractedParams.token2 || "", + amount: parseFloat(intentData.extractedParams.amount || "0"), + sourceAddress: address, + destinationAddress: + intentData.extractedParams.destinationAddress || address, + }, + } + : {}), + ...(["deposit", "withdraw"].includes(intentData.action) + ? { + protocol: intentData.extractedParams.protocol, + fromAmount: intentData.extractedParams.amount, + toAmount: intentData.extractedParams.amount, + receiver: intentData.extractedParams.address, + } + : {}), + }, }; - switch (brianResponse.action) { - case "swap": - case "transfer": - brianResponse.data = { - description: "", - steps: [], - fromToken: { - symbol: brianResponse.extractedParams?.token1 || "", - address: brianResponse.extractedParams?.address || "", - decimals: 1, // default adjust if needed - }, - toToken: { - symbol: brianResponse.extractedParams?.token2 || "", - address: brianResponse.extractedParams?.address || "", - decimals: 1, // default adjust if needed - }, - fromAmount: brianResponse.extractedParams?.amount, - toAmount: brianResponse.extractedParams?.amount, - receiver: brianResponse.extractedParams?.address, - }; - break; - - case "bridge": - brianResponse.data = { - description: "", - steps: [], - bridge: { - sourceNetwork: brianResponse.extractedParams?.chain || "", - destinationNetwork: brianResponse.extractedParams - ?.dest_chain as string, - sourceToken: brianResponse.extractedParams?.token1 || "", - destinationToken: brianResponse.extractedParams?.token2 || "", - amount: parseFloat(brianResponse.extractedParams?.amount || "0"), - sourceAddress: address, - destinationAddress: - brianResponse.extractedParams?.destinationAddress || "", - }, - }; - break; - - case "deposit": - case "withdraw": - brianResponse.data = { - description: "", - steps: [], - protocol: brianResponse.extractedParams?.protocol, - fromAmount: brianResponse.extractedParams?.amount, - toAmount: brianResponse.extractedParams?.amount, - receiver: brianResponse.extractedParams?.address, - }; - break; - - default: - throw new Error(`Unsupported action type: ${brianResponse.action}`); - } - - return brianResponse; + return transactionIntent; } catch (error) { console.error("Error fetching transaction intent:", error); throw error; From f7996e0afea702f057d12621531355acd32603fd Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 23 Jan 2025 23:05:43 +0530 Subject: [PATCH 3/7] used langchain and prompttemplate --- client/app/api/transactions/route.ts | 49 +++++++++++++++------------- client/prompts/prompts.js | 23 +++++++++++++ 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/client/app/api/transactions/route.ts b/client/app/api/transactions/route.ts index bb65acf2..e0c4983a 100644 --- a/client/app/api/transactions/route.ts +++ b/client/app/api/transactions/route.ts @@ -1,12 +1,19 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ + import { NextResponse, NextRequest } from "next/server"; -import OpenAI from "openai"; +import { OpenAI } from "@langchain/openai"; import { transactionProcessor } from "@/lib/transaction"; -import { TRANSACTION_INTENT_PROMPT } from "@/prompts/prompts"; +import type { BrianResponse } from "@/lib/transaction/types"; +import { + TRANSACTION_INTENT_PROMPT, + transactionIntentPromptTemplate, +} from "@/prompts/prompts"; +import { StringOutputParser } from "@langchain/core/output_parsers"; -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY || "", +const llm = new OpenAI({ + model: "gpt-4o-mini", + apiKey: process.env.OPENAI_API_KEY, }); async function getTransactionIntentFromOpenAI( @@ -14,31 +21,28 @@ async function getTransactionIntentFromOpenAI( address: string, chainId: string, messages: any[] -): Promise { +): Promise { try { - const response = await openai.chat.completions.create({ - model: "gpt-4o-mini", - response_format: { type: "json_object" }, - messages: [ - { - role: "system", - content: `${TRANSACTION_INTENT_PROMPT}\n\nAdditional Context:\nCurrent Chain ID: ${chainId}`, - }, - { role: "user", content: prompt }, - ...messages.map((msg) => ({ - role: msg.role, - content: msg.content, - })), - ], + const conversationHistory = messages + .map((msg) => `${msg.role}: ${msg.content}`) + .join("\n"); + + const formattedPrompt = await transactionIntentPromptTemplate.format({ + TRANSACTION_INTENT_PROMPT, + prompt, + chainId, + conversationHistory, }); - const intentData = JSON.parse(response.choices[0].message.content || "{}"); + const jsonOutputParser = new StringOutputParser(); + const response = await llm.pipe(jsonOutputParser).invoke(formattedPrompt); + const intentData = JSON.parse(response); if (!intentData.isTransactionIntent) { throw new Error("Not a transaction-related prompt"); } - const transactionIntent = { + const intentresponse: BrianResponse = { solver: intentData.solver || "OpenAI-Intent-Recognizer", action: intentData.action, type: "write", @@ -51,6 +55,7 @@ async function getTransactionIntentFromOpenAI( protocol: intentData.extractedParams.protocol || "", address: intentData.extractedParams.address || address, dest_chain: intentData.extractedParams.dest_chain || "", + destinationChain: intentData.extractedParams.dest_chain || "", destinationAddress: intentData.extractedParams.destinationAddress || address, }, @@ -99,7 +104,7 @@ async function getTransactionIntentFromOpenAI( }, }; - return transactionIntent; + return intentresponse; } catch (error) { console.error("Error fetching transaction intent:", error); throw error; diff --git a/client/prompts/prompts.js b/client/prompts/prompts.js index 2805ac96..3609db42 100644 --- a/client/prompts/prompts.js +++ b/client/prompts/prompts.js @@ -1,3 +1,5 @@ +import { PromptTemplate } from "@langchain/core/prompts"; + export const ASK_OPENAI_AGENT_PROMPT = ` You are StarkFinder, an expert assistant specializing in the Starknet ecosystem and trading, designed to assist users on our website. You complement BrianAI, the primary knowledge base, by providing additional insights and guidance to users. Your goal is to enhance user understanding and decision-making related to Starknet. @@ -42,3 +44,24 @@ Rules: - Be precise in extracting transaction-specific parameters - Use empty strings for parameters that cannot be determined `; + +export const transactionIntentPromptTemplate = new PromptTemplate({ + inputVariables: ["prompt", "chainId", "conversationHistory"], + template: ` + {TRANSACTION_INTENT_PROMPT} + + dditional Context:s + Current Chain ID: {chainId} + + Conversation History: + {conversationHistory} + + User Prompt: {prompt} + + IMPORTANT: + - Respond ONLY in JSON format + - Ensure all fields are present + - If no transaction intent is detected, set isTransactionIntent to false + - Use empty strings if a parameter is not applicable +`, +}); From 9f339df69ef70d8331686bd3eebd88cfa6538e51 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 23 Jan 2025 23:17:41 +0530 Subject: [PATCH 4/7] changed to langchain --- client/app/api/transactions/route.ts | 4 ++-- client/package-lock.json | 10 +++++----- client/package.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/app/api/transactions/route.ts b/client/app/api/transactions/route.ts index e0c4983a..d51107a6 100644 --- a/client/app/api/transactions/route.ts +++ b/client/app/api/transactions/route.ts @@ -2,7 +2,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { NextResponse, NextRequest } from "next/server"; -import { OpenAI } from "@langchain/openai"; import { transactionProcessor } from "@/lib/transaction"; import type { BrianResponse } from "@/lib/transaction/types"; import { @@ -10,8 +9,9 @@ import { transactionIntentPromptTemplate, } from "@/prompts/prompts"; import { StringOutputParser } from "@langchain/core/output_parsers"; +import { ChatOpenAI } from "@langchain/openai"; -const llm = new OpenAI({ +const llm = new ChatOpenAI({ model: "gpt-4o-mini", apiKey: process.env.OPENAI_API_KEY, }); diff --git a/client/package-lock.json b/client/package-lock.json index 38396c2e..3e266885 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,7 +14,7 @@ "@brian-ai/sdk": "^0.3.6", "@hookform/resolvers": "^3.10.0", "@langchain/anthropic": "^0.3.11", - "@langchain/core": "^0.3.31", + "@langchain/core": "^0.3.33", "@langchain/langgraph": "^0.2.41", "@langchain/openai": "^0.3.17", "@prisma/client": "^6.2.1", @@ -3687,9 +3687,9 @@ } }, "node_modules/@langchain/core": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.31.tgz", - "integrity": "sha512-Fjy8gdaFjGuAW7+ug1XfQYJR3fyWPpWyydPXOhXfjnThaMnHfhIg9kzA3W7DxiMLgAC2fQsAqyxGJVXajV/07A==", + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.33.tgz", + "integrity": "sha512-gIszaRKWmP1HEgOhJLJaMiTMH8U3W9hG6raWihwpCTb0Ns7owjrmaqmgMa9h3W4/0xriaKfrfFBd6tepKsfxZA==", "license": "MIT", "dependencies": { "@cfworker/json-schema": "^4.0.2", @@ -3697,7 +3697,7 @@ "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", - "langsmith": "^0.2.8", + "langsmith": ">=0.2.8 <0.4.0", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", diff --git a/client/package.json b/client/package.json index bb01f62f..11ebb102 100644 --- a/client/package.json +++ b/client/package.json @@ -19,7 +19,7 @@ "@brian-ai/sdk": "^0.3.6", "@hookform/resolvers": "^3.10.0", "@langchain/anthropic": "^0.3.11", - "@langchain/core": "^0.3.31", + "@langchain/core": "^0.3.33", "@langchain/langgraph": "^0.2.41", "@langchain/openai": "^0.3.17", "@prisma/client": "^6.2.1", From a0a6dafb1b09a6bf447f47556a464e18afa0c8aa Mon Sep 17 00:00:00 2001 From: Himanshu Date: Fri, 24 Jan 2025 01:45:36 +0530 Subject: [PATCH 5/7] rectified the error --- client/prompts/prompts.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/prompts/prompts.js b/client/prompts/prompts.js index 3609db42..d7809b32 100644 --- a/client/prompts/prompts.js +++ b/client/prompts/prompts.js @@ -46,7 +46,12 @@ Rules: `; export const transactionIntentPromptTemplate = new PromptTemplate({ - inputVariables: ["prompt", "chainId", "conversationHistory"], + inputVariables: [ + "TRANSACTION_INTENT_PROMPT", + "prompt", + "chainId", + "conversationHistory", + ], template: ` {TRANSACTION_INTENT_PROMPT} From d6621468c38f5fedc8457599fb06d09d478a2bdf Mon Sep 17 00:00:00 2001 From: Himanshu Date: Fri, 24 Jan 2025 04:07:41 +0530 Subject: [PATCH 6/7] minor changes --- client/app/api/transactions/route.ts | 135 +++++++++++++++++---------- client/prompts/prompts.js | 81 +++++++++++----- 2 files changed, 145 insertions(+), 71 deletions(-) diff --git a/client/app/api/transactions/route.ts b/client/app/api/transactions/route.ts index d51107a6..35629850 100644 --- a/client/app/api/transactions/route.ts +++ b/client/app/api/transactions/route.ts @@ -2,18 +2,21 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { NextResponse, NextRequest } from "next/server"; +import { ChatOpenAI } from "@langchain/openai"; import { transactionProcessor } from "@/lib/transaction"; -import type { BrianResponse } from "@/lib/transaction/types"; +import type { + BrianResponse, + BrianTransactionData, +} from "@/lib/transaction/types"; import { TRANSACTION_INTENT_PROMPT, transactionIntentPromptTemplate, } from "@/prompts/prompts"; import { StringOutputParser } from "@langchain/core/output_parsers"; -import { ChatOpenAI } from "@langchain/openai"; const llm = new ChatOpenAI({ - model: "gpt-4o-mini", - apiKey: process.env.OPENAI_API_KEY, + model: "gpt-4", + apiKey: process.env.OPENAI_API_KEY || "", }); async function getTransactionIntentFromOpenAI( @@ -42,7 +45,7 @@ async function getTransactionIntentFromOpenAI( throw new Error("Not a transaction-related prompt"); } - const intentresponse: BrianResponse = { + const intentResponse: BrianResponse = { solver: intentData.solver || "OpenAI-Intent-Recognizer", action: intentData.action, type: "write", @@ -59,52 +62,86 @@ async function getTransactionIntentFromOpenAI( destinationAddress: intentData.extractedParams.destinationAddress || address, }, - data: { - description: "", - steps: [], - ...(["swap", "transfer"].includes(intentData.action) - ? { - fromToken: { - symbol: intentData.extractedParams.token1 || "", - address: intentData.extractedParams.address || "", - decimals: 1, // default, adjust if needed - }, - toToken: { - symbol: intentData.extractedParams.token2 || "", - address: intentData.extractedParams.address || "", - decimals: 1, // default, adjust if needed - }, - fromAmount: intentData.extractedParams.amount, - toAmount: intentData.extractedParams.amount, - receiver: intentData.extractedParams.address, - } - : {}), - ...(intentData.action === "bridge" - ? { - bridge: { - sourceNetwork: intentData.extractedParams.chain || "", - destinationNetwork: intentData.extractedParams.dest_chain || "", - sourceToken: intentData.extractedParams.token1 || "", - destinationToken: intentData.extractedParams.token2 || "", - amount: parseFloat(intentData.extractedParams.amount || "0"), - sourceAddress: address, - destinationAddress: - intentData.extractedParams.destinationAddress || address, - }, - } - : {}), - ...(["deposit", "withdraw"].includes(intentData.action) - ? { - protocol: intentData.extractedParams.protocol, - fromAmount: intentData.extractedParams.amount, - toAmount: intentData.extractedParams.amount, - receiver: intentData.extractedParams.address, - } - : {}), - }, + data: {} as BrianTransactionData, }; - return intentresponse; + switch (intentData.action) { + case "swap": + case "transfer": + intentResponse.data = { + description: intentData.data?.description || "", + steps: + intentData.extractedParams.transaction?.contractAddress || + intentData.extractedParams.transaction?.entrypoint || + intentData.extractedParams.transaction?.calldata + ? [ + { + contractAddress: + intentData.extractedParams.transaction.contractAddress || + "", + entrypoint: + intentData.extractedParams.transaction.entrypoint || + "transfer", + calldata: [ + intentData.extractedParams.destinationAddress || + intentData.extractedParams.address, + intentData.extractedParams.amount, + ], + }, + ] + : [], + fromToken: { + symbol: intentData.extractedParams.token1 || "", + address: intentData.extractedParams.address || "", + decimals: 1, + }, + toToken: { + symbol: intentData.extractedParams.token2 || "", + address: intentData.extractedParams.address || "", + decimals: 1, + }, + fromAmount: intentData.extractedParams.amount, + toAmount: intentData.extractedParams.amount, + receiver: intentData.extractedParams.address, + amountToApprove: intentData.data?.amountToApprove, + gasCostUSD: intentData.data?.gasCostUSD, + }; + break; + + case "bridge": + intentResponse.data = { + description: "", + steps: [], + bridge: { + sourceNetwork: intentData.extractedParams.chain || "", + destinationNetwork: intentData.extractedParams.dest_chain || "", + sourceToken: intentData.extractedParams.token1 || "", + destinationToken: intentData.extractedParams.token2 || "", + amount: parseFloat(intentData.extractedParams.amount || "0"), + sourceAddress: address, + destinationAddress: + intentData.extractedParams.destinationAddress || address, + }, + }; + break; + + case "deposit": + case "withdraw": + intentResponse.data = { + description: "", + steps: [], + protocol: intentData.extractedParams.protocol || "", + fromAmount: intentData.extractedParams.amount, + toAmount: intentData.extractedParams.amount, + receiver: intentData.extractedParams.address || "", + }; + break; + + default: + throw new Error(`Unsupported action type: ${intentData.action}`); + } + + return intentResponse; } catch (error) { console.error("Error fetching transaction intent:", error); throw error; diff --git a/client/prompts/prompts.js b/client/prompts/prompts.js index d7809b32..ae0d58c2 100644 --- a/client/prompts/prompts.js +++ b/client/prompts/prompts.js @@ -17,32 +17,69 @@ Your responsibilities: NOTE: On the website, always refer to yourself as "StarkFinder." Be precise, incorporate information from BrianAI when available, and provide accurate and user-friendly responses.`; export const TRANSACTION_INTENT_PROMPT = ` -You are a blockchain transaction intent recognition system. -Given a user prompt, analyze and determine if the request involves a blockchain transaction. +You are a blockchain transaction intent recognition system. +Given a user prompt, analyze and determine if the request involves a blockchain transaction. Respond ONLY in JSON format with the following structure: { - "isTransactionIntent": boolean, - "solver": string, - "action": "swap" | "transfer" | "deposit" | "withdraw" | "bridge", - "type": "write", - "extractedParams": { - "action": string, - "token1": string, - "token2": string, - "chain": string, - "dest_chain": string, - "amount": string, - "protocol": string, - "address": string, - "destinationAddress": string - } + "isTransactionIntent": boolean, + "solver": string, + "action": "swap" | "transfer" | "deposit" | "withdraw" | "bridge", + "type": "write", + "extractedParams": { + "action": string, + "token1": string, + "token2": string, + "chain": string, + "dest_chain": string, + "amount": string, + "protocol": string, + "address": string, + "destinationAddress": string, + "transaction": { + "contractAddress": string, + "entrypoint": string, + "calldata": string[] + } + } } -Rules: -- If the prompt is NOT a transaction-related request, set isTransactionIntent to false -- Be precise in extracting transaction-specific parameters -- Use empty strings for parameters that cannot be determined +Transaction Analysis Guidelines: +1. Accurately identify the type of transaction from the user's intent +2. Extract precise transaction parameters +3. Include transaction details in the 'transaction' field when applicable +4. Use empty strings for parameters that cannot be determined + +Examples: +1. "Send 0.1 ETH to 0x123..." + - action: "transfer" + - token1: "ETH" + - amount: "0.1" + - address: "0x123..." + - transaction: { + contractAddress: "", + entrypoint: "transfer", + calldata: ["0x123...", "0.1"] + } + +2. "Bridge 50 USDC from Ethereum to Arbitrum" + - action: "bridge" + - token1: "USDC" + - chain: "Ethereum" + - dest_chain: "Arbitrum" + - amount: "50" + - transaction: { + contractAddress: "", + entrypoint: "bridge", + calldata: ["USDC", "50", "Ethereum", "Arbitrum"] + } + +Current Context: +- User Prompt: {prompt} +- Connected Chain ID: {chainId} +- Conversation History: {conversationHistory} + +Analyze the intent carefully and provide the most accurate transaction representation possible. `; export const transactionIntentPromptTemplate = new PromptTemplate({ @@ -55,7 +92,7 @@ export const transactionIntentPromptTemplate = new PromptTemplate({ template: ` {TRANSACTION_INTENT_PROMPT} - dditional Context:s + dditional Context: Current Chain ID: {chainId} Conversation History: From e6afd728ce4210e5338b29c6f0877ddb6b92bf18 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Fri, 24 Jan 2025 14:09:28 +0530 Subject: [PATCH 7/7] solved transaction errors --- client/app/api/transactions/route.ts | 9 ++++---- client/prompts/prompts.js | 31 ++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/client/app/api/transactions/route.ts b/client/app/api/transactions/route.ts index 35629850..cc928907 100644 --- a/client/app/api/transactions/route.ts +++ b/client/app/api/transactions/route.ts @@ -16,7 +16,7 @@ import { StringOutputParser } from "@langchain/core/output_parsers"; const llm = new ChatOpenAI({ model: "gpt-4", - apiKey: process.env.OPENAI_API_KEY || "", + apiKey: process.env.OPENAI_API_KEY, }); async function getTransactionIntentFromOpenAI( @@ -77,15 +77,14 @@ async function getTransactionIntentFromOpenAI( ? [ { contractAddress: - intentData.extractedParams.transaction.contractAddress || - "", + intentData.extractedParams.transaction.contractAddress, entrypoint: - intentData.extractedParams.transaction.entrypoint || - "transfer", + intentData.extractedParams.transaction.entrypoint, calldata: [ intentData.extractedParams.destinationAddress || intentData.extractedParams.address, intentData.extractedParams.amount, + "0", ], }, ] diff --git a/client/prompts/prompts.js b/client/prompts/prompts.js index ae0d58c2..d305cea2 100644 --- a/client/prompts/prompts.js +++ b/client/prompts/prompts.js @@ -41,7 +41,17 @@ Respond ONLY in JSON format with the following structure: "entrypoint": string, "calldata": string[] } - } + }, + "data": { + "description": string, + "steps": [ + { + "contractAddress": string, + "entrypoint": string, + "calldata": string[] + } + ] + } } Transaction Analysis Guidelines: @@ -56,10 +66,21 @@ Examples: - token1: "ETH" - amount: "0.1" - address: "0x123..." - - transaction: { - contractAddress: "", + - data.steps: { + contractAddress: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + entrypoint: "transfer", + calldata: ["0x123...", "0.1","0"] + } + +2. "Send 0.1 STRK to 0x123..." + - action: "transfer" + - token1: "STRK" + - amount: "0.1" + - address: "0x123..." + - data.steps: { + contractAddress: "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", entrypoint: "transfer", - calldata: ["0x123...", "0.1"] + calldata: ["0x123...", "0.1","0"] } 2. "Bridge 50 USDC from Ethereum to Arbitrum" @@ -74,6 +95,8 @@ Examples: calldata: ["USDC", "50", "Ethereum", "Arbitrum"] } +remember to take contract address based on type of token as there are different address for STRK and ETH that i have provided + Current Context: - User Prompt: {prompt} - Connected Chain ID: {chainId}