Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0g plugin dev #1

Merged
merged 7 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,9 @@ SERVER_PORT=3000
# Starknet
STARKNET_ADDRESS=
STARKNET_PRIVATE_KEY=

#ZeroG
ZEROG_INDEXER_RPC=
ZEROG_EVM_RPC=
ZEROG_PRIVATE_KEY=
ZEROG_FLOW_ADDRESS=
9,105 changes: 9,105 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"node": ">=22"
},
"dependencies": {
"@0glabs/0g-ts-sdk": "^0.2.1",
"ollama-ai-provider": "^0.16.1",
"optional": "^0.1.4",
"sharp": "^0.33.5"
Expand Down
1 change: 1 addition & 0 deletions packages/agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@ai16z/plugin-image-generation": "workspace:*",
"@ai16z/plugin-node": "workspace:*",
"@ai16z/plugin-solana": "workspace:*",
"@ai16z/plugin-0g": "workspace:*",
"readline": "^1.3.0",
"ws": "^8.18.0",
"yargs": "17.7.2"
Expand Down
2 changes: 2 additions & 0 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from "@ai16z/eliza";
import { bootstrapPlugin } from "@ai16z/plugin-bootstrap";
import { solanaPlugin } from "@ai16z/plugin-solana";
import { zgPlugin } from "@ai16z/plugin-0g";
import { nodePlugin } from "@ai16z/plugin-node";
import Database from "better-sqlite3";
import fs from "fs";
Expand Down Expand Up @@ -234,6 +235,7 @@ export async function createAgent(
bootstrapPlugin,
nodePlugin,
character.settings.secrets?.WALLET_PUBLIC_KEY ? solanaPlugin : null,
zgPlugin,
].filter(Boolean),
providers: [],
actions: [],
Expand Down
17 changes: 17 additions & 0 deletions packages/plugin-0g/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@ai16z/plugin-0g",
"version": "0.0.1",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@ai16z/eliza": "workspace:*",
"tsup": "^8.3.5",
"@0glabs/0g-ts-sdk": "0.2.1",
"ethers": "^6.0.0"
},
"scripts": {
"build": "tsup --format esm --dts",
"test": "vitest"
}
}
185 changes: 185 additions & 0 deletions packages/plugin-0g/src/actions/upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import {
Action,
HandlerCallback,
IAgentRuntime,
Memory,
State,
ModelClass,
Content,
ActionExample
} from "@ai16z/eliza";
import { Indexer, ZgFile, getFlowContract } from '@0glabs/0g-ts-sdk';
import { ethers } from 'ethers';
import { composeContext } from "@ai16z/eliza";
import { generateObject } from "@ai16z/eliza";
import { promises as fs } from 'fs';


const uploadTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.

Example response:
\`\`\`json
{
"filePath": null,
"description": "I want to upload a file"
}
\`\`\`

{{recentMessages}}

Extract the user's intention to upload a file from the conversation. Users might express this in various ways, such as:
- "I want to upload a file"
- "upload an image"
- "send a photo"
- "upload"
- "let me share a file"

If the user provides any specific description of the file, include that as well.

Respond with a JSON markdown block containing only the extracted values.`;

export interface UploadContent extends Content {
filePath: string;
}

function isUploadContent(
_runtime: IAgentRuntime,
content: any
): content is UploadContent {
console.log("Content for upload", content);
return (
typeof content.filePath === "string"
);
}

export const zgUpload: Action = {
name: "ZG_UPLOAD",
similes: [
"UPLOAD_FILE_TO_ZG",
"STORE_FILE_ON_ZG",
"SAVE_FILE_TO_ZG",
"UPLOAD_TO_ZERO_GRAVITY",
"STORE_ON_ZERO_GRAVITY",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ZERO_GRAVITY vs ZG, let's make it consistent

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Various of similes make the agent smarter?

"SHARE_FILE_ON_ZG",
"PUBLISH_FILE_TO_ZG"
],
description: "Store data using 0G protocol",
validate: async (runtime: IAgentRuntime, message: Memory) => {
const zgIndexerRpc = !!runtime.getSetting("ZEROG_INDEXER_RPC");
const zgEvmRpc = !!runtime.getSetting("ZEROG_EVM_RPC");
const zgPrivateKey = !!runtime.getSetting("ZEROG_PRIVATE_KEY");
const flowAddr = !!runtime.getSetting("ZEROG_FLOW_ADDRESS");
return zgIndexerRpc && zgEvmRpc && zgPrivateKey && flowAddr;
},
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
options: any,
callback: HandlerCallback
) => {
console.log("ZG_UPLOAD action called");
if (!state) {
state = (await runtime.composeState(message)) as State;
} else {
state = await runtime.updateRecentMessageState(state);
}

// Compose upload context
const uploadContext = composeContext({
state,
template: uploadTemplate,
});

// Generate upload content
const content = await generateObject({
runtime,
context: uploadContext,
modelClass: ModelClass.SMALL,
});

// Validate upload content
if (!isUploadContent(runtime, content)) {
console.error("Invalid content for UPLOAD action.");
if (callback) {
callback({
text: "Unable to process 0G upload request. Invalid content provided.",
content: { error: "Invalid upload content" },
});
}
return false;
}

try {
const zgIndexerRpc = runtime.getSetting("ZEROG_INDEXER_RPC");
const zgEvmRpc = runtime.getSetting("ZEROG_EVM_RPC");
const zgPrivateKey = runtime.getSetting("ZEROG_PRIVATE_KEY");
const flowAddr = runtime.getSetting("ZEROG_FLOW_ADDRESS");
const filePath = content.filePath;
if (!filePath) {
console.error("File path is missing");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File path is required

return false;
}

// Check if file exists and is accessible
try {
await fs.access(filePath);
} catch (error) {
console.error(`File ${filePath} does not exist or is not accessible:`, error);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vs ", make it consistent. also a space afteraccessible:`

return false;
}

const file = await ZgFile.fromFilePath(filePath);
var [tree, err] = await file.merkleTree();
if (err === null) {
console.log("File Root Hash: ", tree.rootHash());
} else {
console.log("Error getting file root hash: ", err);
return false;
}

const provider = new ethers.JsonRpcProvider(zgEvmRpc);
const signer = new ethers.Wallet(zgPrivateKey, provider);
const indexer = new Indexer(zgIndexerRpc);
const flowContract = getFlowContract(flowAddr, signer);

var [tx, err] = await indexer.upload(file, 0, zgEvmRpc, flowContract);
if (err === null) {
console.log("File uploaded successfully, tx: ", tx);
} else {
console.log("Error uploading file: ", err);
return false;
}

await file.close();

} catch (error) {
console.error("Error getting settings for 0G upload:", error);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after upload:

}
},
examples: [[
{
user: "{{user1}}",
content: {
text: "upload my resume.pdf file",
action: "ZG_UPLOAD"
}
}
], [
{
user: "{{user1}}",
content: {
text: "can you help me upload this document.docx?",
action: "ZG_UPLOAD"
}
}
], [
{
user: "{{user1}}",
content: {
text: "I need to upload an image file image.png",
action: "ZG_UPLOAD"
}
}
]] as ActionExample[][],
} as Action;
10 changes: 10 additions & 0 deletions packages/plugin-0g/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Plugin } from "@ai16z/eliza";
import { zgUpload } from "./actions/upload";

export const zgPlugin: Plugin = {
name: "ZG",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the name here be ZeroG or 0G

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

description: "0G Plugin for Eliza",
actions: [zgUpload],
evaluators: [],
providers: [],
};
9 changes: 9 additions & 0 deletions packages/plugin-0g/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": ".",
"types": ["node"]
},
"include": ["src"]
}
13 changes: 13 additions & 0 deletions packages/plugin-0g/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
sourcemap: true,
clean: true,
format: ["esm"], // Ensure you're targeting CommonJS
external: [
"@0glabs/0g-ts-sdk",
// Add other modules you want to externalize
],
});
Loading