forked from langchain-ai/langchainjs
-
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.
Added Hedera tool integration, and hedera doc
- Loading branch information
MuhannadJam
committed
Nov 24, 2024
1 parent
cb2c42c
commit 6337983
Showing
14 changed files
with
1,070 additions
and
15 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,29 @@ | ||
--- | ||
hide_table_of_contents: true | ||
--- | ||
|
||
import CodeBlock from "@theme/CodeBlock"; | ||
|
||
# Hedera Tool | ||
|
||
The Hedera Tool gives your agent the ability to create an account, delete an account, transfer | ||
between accounts, and query the balance of an account. | ||
|
||
## Setup | ||
|
||
To use the Hedera Tool you need to install the following official peer depencency: | ||
|
||
```bash npm2yarn | ||
npm install @hashgraph/sdk | ||
``` | ||
|
||
## Usage | ||
|
||
import ToolExample from "@examples/tools/hedera.ts"; | ||
|
||
<CodeBlock language="typescript">{ToolExample}</CodeBlock> | ||
|
||
## Related | ||
|
||
- Tool [conceptual guide](/docs/concepts/tools) | ||
- Tool [how-to guides](/docs/how_to/#tools) |
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,83 @@ | ||
import { | ||
HederaAccountBalance, | ||
HederaTransfer, | ||
HederaCreateAccount, | ||
HederaDeleteAccount, | ||
} from "@langchain/community/tools/hedera"; | ||
import { PrivateKey } from "@hashgraph/sdk" | ||
import { StructuredTool } from "@langchain/core/tools"; | ||
import { ChatOpenAI } from "@langchain/openai"; | ||
import type { ChatPromptTemplate } from "@langchain/core/prompts"; | ||
import { createOpenAIFunctionsAgent, AgentExecutor } from "langchain/agents"; | ||
import { pull } from "langchain/hub"; | ||
import { tr } from "@faker-js/faker"; | ||
|
||
export async function run() { | ||
const llm = new ChatOpenAI({ | ||
model: "gpt-3.5-turbo", | ||
temperature: 0, | ||
}); | ||
|
||
// Initalize paramters for hedera tools to use | ||
const hederaParams = { | ||
credentials: { | ||
accountId: process.env.HEDERA_ACCOUNT_ID, | ||
privateKey: process.env.HEDERA_PRIVATE_KEY, | ||
}, | ||
network: "testnet", | ||
maxTransactionFee: 100, | ||
maxQueryPayment: 50, | ||
}; | ||
|
||
// Provide the hedera tools to be used | ||
const tools: StructuredTool[] = [ | ||
new HederaAccountBalance(hederaParams), | ||
new HederaTransfer(hederaParams), | ||
new HederaCreateAccount(hederaParams), | ||
new HederaDeleteAccount(hederaParams), | ||
]; | ||
|
||
// Setup the agent to use the hedera tool | ||
const prompt = await pull<ChatPromptTemplate>( | ||
"hwchase17/openai-functions-agent" | ||
); | ||
|
||
const agent = await createOpenAIFunctionsAgent({ | ||
llm, | ||
tools, | ||
prompt, | ||
}); | ||
|
||
const agentExecutor = new AgentExecutor({ | ||
agent, | ||
tools, | ||
verbose: false, | ||
}); | ||
|
||
const newAccountPrivateKey = PrivateKey.generateED25519(); | ||
const newAccountPublicKey = newAccountPrivateKey.publicKey; | ||
|
||
// Create hedera account | ||
const result1 = await agentExecutor.invoke({ | ||
input: `Can you create an account with public key ${newAccountPublicKey}`, | ||
}); | ||
console.log(result1.output); | ||
|
||
// Get the account balance of client operator | ||
const result2 = await agentExecutor.invoke({ | ||
input: `What is my account balance`, | ||
}); | ||
console.log(result2.output); | ||
|
||
// Get the account balance of a specific account | ||
const result3 = await agentExecutor.invoke({ | ||
input: `What is the account balance of 0.0.1111111`, | ||
}); | ||
console.log(result3.output); | ||
|
||
// Transfer tinybars between accounts | ||
const result4 = await agentExecutor.invoke({ | ||
input: `Can you transfer 100000000 tinybars from my account to 0.0.1111111`, | ||
}); | ||
console.log(result4.output); | ||
} |
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
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
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,87 @@ | ||
import { Client, Hbar } from "@hashgraph/sdk"; | ||
import { z } from "zod"; | ||
import { StructuredTool } from "@langchain/core/tools"; | ||
import { getEnvironmentVariable } from "@langchain/core/utils/env"; | ||
|
||
export interface HederaBaseToolParams { | ||
credentials?: { | ||
accountId?: string; | ||
privateKey?: string; | ||
}; | ||
network?: string; | ||
maxTransactionFee?: number; | ||
maxQueryPayment?: number; | ||
} | ||
|
||
export abstract class HederaBaseTool extends StructuredTool { | ||
private CredentialsSchema = z | ||
.object({ | ||
accountId: z | ||
.string() | ||
.min(1) | ||
.default(getEnvironmentVariable("HEDERA_ACCOUNT_ID") ?? ""), | ||
privateKey: z | ||
.string() | ||
.min(1) | ||
.default(getEnvironmentVariable("HEDERA_PRIVATE_KEY") ?? ""), | ||
}) | ||
.refine( | ||
(credentials: { accountId: string; privateKey: string }) => | ||
credentials.accountId !== "" || credentials.privateKey !== "", | ||
{ | ||
message: | ||
"Missing HEDERA_ACCOUNT_ID or HEDERA_PRIVATE_KEY to interact with Hedera", | ||
} | ||
); | ||
|
||
private HederaBaseToolParamsSchema = z | ||
.object({ | ||
credentials: this.CredentialsSchema.default({}), | ||
network: z.enum(["mainnet", "testnet", "previewnet"]).default("testnet"), | ||
maxTransactionFee: z.number().default(100), | ||
maxQueryPayment: z.number().default(50), | ||
}) | ||
.default({}); | ||
|
||
name = "Hedera Tool"; | ||
|
||
description = "A tool for interacting with the Hedera network."; | ||
|
||
protected client: Client; | ||
|
||
constructor(fields?: Partial<HederaBaseToolParams>) { | ||
super(...arguments); | ||
|
||
const { credentials, network, maxTransactionFee, maxQueryPayment } = | ||
this.HederaBaseToolParamsSchema.parse(fields); | ||
|
||
this.client = this.getHederaClient( | ||
network, | ||
credentials.accountId, | ||
credentials.privateKey, | ||
maxTransactionFee, | ||
maxQueryPayment | ||
); | ||
} | ||
|
||
private getHederaClient( | ||
network: "mainnet" | "testnet" | "previewnet", | ||
accountId: string, | ||
privateKey: string, | ||
maxTransactionFee: number, | ||
maxQueryPayment: number | ||
): Client { | ||
let client: Client; | ||
if (network === "mainnet") { | ||
client = Client.forMainnet(); | ||
} else if (network === "testnet") { | ||
client = Client.forTestnet(); | ||
} else { | ||
client = Client.forPreviewnet(); | ||
} | ||
client.setOperator(accountId, privateKey); | ||
client.setDefaultMaxTransactionFee(new Hbar(maxTransactionFee)); | ||
client.setDefaultMaxQueryPayment(new Hbar(maxQueryPayment)); | ||
return client; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
libs/langchain-community/src/tools/hedera/create_account.ts
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,59 @@ | ||
import { | ||
AccountCreateTransaction, | ||
AccountBalanceQuery, | ||
Hbar, | ||
PublicKey, | ||
} from "@hashgraph/sdk"; | ||
import { z } from "zod"; | ||
import { HederaBaseTool, HederaBaseToolParams } from "./base.js"; | ||
|
||
export class HederaCreateAccount extends HederaBaseTool { | ||
name = "hedera_create_account"; | ||
|
||
schema = z.object({ | ||
newAccountPublicKey: z.string(), | ||
initialAmount: z.number().default(1000), | ||
}); | ||
|
||
description = `A tool for creating a new Hedera account. Takes the new account's public key and the | ||
intial amount to set the starting balance of the account to. If no inital amount is provided | ||
it will set the starting balance to 1000 tinybar.`; | ||
|
||
constructor(fields?: HederaBaseToolParams) { | ||
super(fields); | ||
} | ||
|
||
async _call(arg: z.output<typeof this.schema>): Promise<string> { | ||
const { newAccountPublicKey, initialAmount } = arg; | ||
|
||
try { | ||
const publicKey = PublicKey.fromString(newAccountPublicKey); | ||
const newAccount = await new AccountCreateTransaction() | ||
.setKey(publicKey) | ||
.setInitialBalance(Hbar.fromTinybars(initialAmount)) | ||
.execute(this.client); | ||
|
||
const getReceipt = await newAccount.getReceipt(this.client); | ||
const newAccountId = getReceipt.accountId; | ||
|
||
if (!newAccountId) { | ||
throw new Error("Account creation failed."); | ||
} | ||
|
||
const accountBalance = await new AccountBalanceQuery() | ||
.setAccountId(newAccountId) | ||
.execute(this.client); | ||
|
||
return `Account created successfully. Account ID: ${newAccountId}, | ||
Initial Balance: ${accountBalance.hbars.toString()} tinybars`; | ||
} catch (error) { | ||
const typedError = error as Error; | ||
throw new Error(`Failed to create account: ${typedError.message}`); | ||
} | ||
} | ||
} | ||
|
||
export type AccountCreateSchema = { | ||
newAccountPublicKey: string; | ||
initialAmount?: number; | ||
}; |
52 changes: 52 additions & 0 deletions
52
libs/langchain-community/src/tools/hedera/delete_account.ts
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,52 @@ | ||
import { AccountDeleteTransaction, PrivateKey } from "@hashgraph/sdk"; | ||
import { z } from "zod"; | ||
import { HederaBaseTool, HederaBaseToolParams } from "./base.js"; | ||
|
||
export class HederaDeleteAccount extends HederaBaseTool { | ||
name = "hedera_delete_account"; | ||
|
||
schema = z.object({ | ||
accountId: z.string(), | ||
accountPrivateKey: z.string(), | ||
}); | ||
|
||
description = `A tool for deleting a Hedera account. Takes the account id and the | ||
private key of the account to delete.`; | ||
|
||
constructor(fields?: HederaBaseToolParams) { | ||
super(fields); | ||
} | ||
|
||
async _call(arg: z.output<typeof this.schema>): Promise<string> { | ||
const { accountId, accountPrivateKey } = arg; | ||
|
||
try { | ||
const operatorAccountId = this.client.operatorAccountId?.toString(); | ||
|
||
if (!operatorAccountId) { | ||
throw new Error("Operator account ID is null."); | ||
} | ||
|
||
const transaction = await new AccountDeleteTransaction() | ||
.setAccountId(accountId) | ||
.setTransferAccountId(operatorAccountId) | ||
.freezeWith(this.client); | ||
|
||
const privateKey = PrivateKey.fromStringED25519(accountPrivateKey); | ||
const signTx = await transaction.sign(privateKey); | ||
const txResponse = await signTx.execute(this.client); | ||
const receipt = await txResponse.getReceipt(this.client); | ||
const transactionStatus = receipt.status; | ||
|
||
return `Deleted account ${accountId} ${transactionStatus}`; | ||
} catch (error) { | ||
const typedError = error as Error; | ||
throw new Error(`Failed to delete account: ${typedError.message}`); | ||
} | ||
} | ||
} | ||
|
||
export type AccountDeleteSchema = { | ||
accountId: string; | ||
accountPrivateKey: string; | ||
}; |
Oops, something went wrong.