Skip to content

Commit

Permalink
feat: Implement framework-independent Action interface (#72)
Browse files Browse the repository at this point in the history
This PR implements a framework-independent Action interface inspired by
Eliza, making the tools more flexible and reusable across different
frameworks.

Changes:
- Created independent actions under `src/actions/`
- Implemented Zod validation for type safety
- Converted all LangChain tools to use actions
- Added natural language matching with similes
- Improved error handling and response formats
  • Loading branch information
thearyanag authored Dec 31, 2024
2 parents 15034ac + 9bf1755 commit 16f7dc6
Show file tree
Hide file tree
Showing 40 changed files with 2,540 additions and 18 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
"form-data": "^4.0.1",
"langchain": "^0.3.8",
"openai": "^4.77.0",
"typedoc": "^0.27.6"
"typedoc": "^0.27.6",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/bn.js": "^5.1.6",
Expand Down
21 changes: 4 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 62 additions & 0 deletions src/actions/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { get_balance } from "../tools";

const balanceAction: Action = {
name: "solana_balance",
similes: [
"check balance",
"get wallet balance",
"view balance",
"show balance",
"check token balance",
],
description: `Get the balance of a Solana wallet or token account.
If you want to get the balance of your wallet, you don't need to provide the tokenAddress.
If no tokenAddress is provided, the balance will be in SOL.`,
examples: [
[
{
input: {},
output: {
status: "success",
balance: "100",
token: "SOL",
},
explanation: "Get SOL balance of the wallet",
},
],
[
{
input: {
tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
},
output: {
status: "success",
balance: "1000",
token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
},
explanation: "Get USDC token balance",
},
],
],
schema: z.object({
tokenAddress: z.string().optional(),
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const balance = await get_balance(
agent,
input.tokenAddress && new PublicKey(input.tokenAddress),
);

return {
status: "success",
balance: balance,
token: input.tokenAddress || "SOL",
};
},
};

export default balanceAction;
104 changes: 104 additions & 0 deletions src/actions/compressedAirdrop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { sendCompressedAirdrop } from "../tools";

const compressedAirdropAction: Action = {
name: "solana_compressed_airdrop",
similes: [
"ZK Compressed airdrop",
"Airdrop tokens with compression",
"Send compressed SPL airdrop",
"Airdrop to multiple recipients",
],
description:
"Airdrop SPL tokens with ZK Compression (also known as airdropping tokens) to multiple recipients",
examples: [
[
{
input: {
mintAddress: "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
amount: 42,
decimals: 6,
recipients: [
"1nc1nerator11111111111111111111111111111111",
"BrFndAe111111111111111111111111111111111",
],
priorityFeeInLamports: 30000,
shouldLog: true,
},
output: {
status: "success",
message: "Airdropped 42 tokens to 2 recipients.",
transactionHashes: ["4uyfBN...", "9XsF2N..."],
},
explanation:
"Airdrops 42 tokens (with 6 decimals) to 2 recipients, optionally logging progress to stdout.",
},
],
],
// Validate inputs with zod
schema: z.object({
mintAddress: z
.string()
.min(1)
.describe("Mint address of the token, e.g., 'JUPy...'"),
amount: z
.number()
.positive()
.describe("Number of tokens to airdrop per recipient, e.g., 42"),
decimals: z
.number()
.nonnegative()
.int()
.describe("Decimals of the token, e.g., 6"),
recipients: z
.array(z.string())
.nonempty()
.describe("Array of recipient addresses, e.g., ['1nc1n...']"),
priorityFeeInLamports: z
.number()
.optional()
.describe("Priority fee in lamports (default is 30_000)"),
shouldLog: z
.boolean()
.optional()
.describe("Whether to log progress to stdout (default is false)"),
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const {
mintAddress,
amount,
decimals,
recipients,
priorityFeeInLamports,
shouldLog,
} = input;

// Call your airdrop method on the SolanaAgentKit
const txs = await sendCompressedAirdrop(
mintAddress,
amount,
decimals,
recipients,
priorityFeeInLamports || 30_000,
shouldLog || false,
);

return {
status: "success",
message: `Airdropped ${amount} tokens to ${recipients.length} recipients.`,
transactionHashes: txs,
};
} catch (error: any) {
return {
status: "error",
message: `Failed to airdrop tokens: ${error.message}`,
code: error.code || "UNKNOWN_ERROR",
};
}
},
};

export default compressedAirdropAction;
86 changes: 86 additions & 0 deletions src/actions/createGibworkTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { create_gibwork_task } from "../tools";

const createGibworkTaskAction: Action = {
name: "solana_create_gibwork_task",
similes: [
"create task",
"post job",
"create gig",
"post task",
"create work",
"new task on gibwork",
],
description:
"Create a new task on the Gibwork platform with payment in SPL tokens",
examples: [
[
{
input: {
title: "Build a Solana dApp",
content: "Create a simple Solana dApp with React frontend",
requirements: "Experience with Rust and React",
tags: ["solana", "rust", "react"],
tokenMintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
tokenAmount: 100,
},
output: {
status: "success",
taskId: "task_123",
signature: "3YKpM1...",
message: "Successfully created task: Build a Solana dApp",
},
explanation: "Create a new task on Gibwork with 100 USDC payment",
},
],
],
schema: z.object({
title: z.string().min(1).describe("Title of the task"),
content: z.string().min(1).describe("Description of the task"),
requirements: z
.string()
.min(1)
.describe("Requirements to complete the task"),
tags: z
.array(z.string())
.min(1)
.describe("List of tags associated with the task"),
tokenMintAddress: z.string().describe("Token mint address for payment"),
tokenAmount: z.number().positive().describe("Payment amount for the task"),
payer: z
.string()
.optional()
.describe("Optional payer address (defaults to wallet address)"),
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const responseData = await create_gibwork_task(
agent,
input.title,
input.content,
input.requirements,
input.tags,
new PublicKey(input.tokenMintAddress),
input.tokenAmount,
input.payer ? new PublicKey(input.payer) : undefined,
);

return {
status: "success",
taskId: responseData.taskId,
signature: responseData.signature,
message: `Successfully created task: ${input.title}`,
};
} catch (error: any) {
return {
status: "error",
message: `Failed to create task: ${error.message}`,
};
}
},
};

export default createGibworkTaskAction;
Loading

0 comments on commit 16f7dc6

Please sign in to comment.