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

feat: Add Text to 3D function #1446

Merged
merged 4 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@elizaos/plugin-multiversx": "workspace:*",
"@elizaos/plugin-near": "workspace:*",
"@elizaos/plugin-zksync-era": "workspace:*",
"@elizaos/plugin-3d-generation": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"yargs": "17.7.2"
Expand Down
4 changes: 4 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { evmPlugin } from "@elizaos/plugin-evm";
import { storyPlugin } from "@elizaos/plugin-story";
import { flowPlugin } from "@elizaos/plugin-flow";
import { imageGenerationPlugin } from "@elizaos/plugin-image-generation";
import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation";
import { multiversxPlugin } from "@elizaos/plugin-multiversx";
import { nearPlugin } from "@elizaos/plugin-near";
import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation";
Expand Down Expand Up @@ -516,6 +517,9 @@ export async function createAgent(
getSecret(character, "HEURIST_API_KEY")
? imageGenerationPlugin
: null,
getSecret(character, "FAL_API_KEY")
? ThreeDGenerationPlugin
: null,
...(getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY")
? [
Expand Down
7 changes: 7 additions & 0 deletions packages/plugin-3d-generation/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*

!dist/**
!package.json
!readme.md
!tsup.config.ts
!tsconfig.json
3 changes: 3 additions & 0 deletions packages/plugin-3d-generation/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import eslintGlobalConfig from "../../eslint.config.mjs";

export default [...eslintGlobalConfig];
19 changes: 19 additions & 0 deletions packages/plugin-3d-generation/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@elizaos/plugin-3d-generation",
"version": "0.1.7-alpha.1",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*",
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"lint": "eslint --fix --cache ."
},
"peerDependencies": {
"whatwg-url": "7.1.0"
}
}
4 changes: 4 additions & 0 deletions packages/plugin-3d-generation/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const FAL_CONSTANTS = {
API_3D_ENDPOINT: "fal-ai/hyper3d/rodin",
API_KEY_SETTING: "FAL_API_KEY", // The setting name to fetch from runtime
};
198 changes: 198 additions & 0 deletions packages/plugin-3d-generation/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { elizaLogger } from "@elizaos/core";
import {
Action,
HandlerCallback,
IAgentRuntime,
Memory,
Plugin,
State,
} from "@elizaos/core";
import { fal } from "@fal-ai/client";
import { FAL_CONSTANTS } from "./constants";

import * as fs from 'fs';
import { Buffer } from 'buffer';
import * as path from 'path';
import * as process from 'process';

const generate3D = async (prompt: string, runtime: IAgentRuntime) => {
process.env['FAL_KEY'] = FAL_CONSTANTS.API_KEY_SETTING || runtime.getSetting("FAL_API_KEY");

try {
elizaLogger.log("Starting 3D generation with prompt:", prompt);

const response = await fal.subscribe(FAL_CONSTANTS.API_3D_ENDPOINT, {
input: {
prompt: prompt,
input_image_urls: [],
condition_mode: "concat", // fuse concat
geometry_file_format: "glb", // glb usdz fbx obj stl
material: "PBR", // PBR Shaded
quality: "medium", // extra-low, low, medium, high
tier: "Regular" // Regular, Sketch
},
logs: true,
onQueueUpdate: (update) => {
if (update.status === "IN_PROGRESS") {
update.logs.map((log) => log.message).forEach(elizaLogger.log);
}
},
});


elizaLogger.log(
"Generation request successful, received response:",
response
);


return {success: true,
url: response.data.model_mesh.url,
file_name: response.data.model_mesh.file_name};

} catch (error) {
elizaLogger.error("3D generation error:", error);
return {
success: false,
error: error.message || "Unknown error occurred",
};
}
};

const ThreeDGeneration: Action = {
name: "GENERATE_3D",
similes: [
"3D_GENERATION",
"3D_GEN",
"CREATE_3D",
"MAKE_3D",
"TEXT23D",
"TEXT_TO_3D",
"3D_CREATE",
"3D_MAKE",
],
description: "Generate a 3D object based on a text prompt",
validate: async (runtime: IAgentRuntime, _message: Memory) => {
elizaLogger.log("Validating 3D generation action");
const FalApiKey = runtime.getSetting("FAL_API_KEY");
elizaLogger.log("FAL_API_KEY present:", !!FalApiKey);
return !!FalApiKey;
},
handler: async (
runtime: IAgentRuntime,
message: Memory,
_state: State,
_options: any,
callback: HandlerCallback
) => {
elizaLogger.log("3D generation request:", message);

// Clean up the prompt by removing mentions and commands
const ThreeDPrompt = message.content.text
.replace(/<@\d+>/g, "") // Remove mentions
.replace(
/generate 3D|create 3D|make 3D|render 3D/gi,
""
) // Remove commands
.trim();

if (!ThreeDPrompt || ThreeDPrompt.length < 3) {
callback({
text: "Could you please provide more details about what kind of 3D object you'd like me to generate? For example: 'Generate a lovely cat'",
});
return;
}

elizaLogger.log("3D prompt:", ThreeDPrompt);

callback({
text: `I'll generate a 3D object based on your prompt: "${ThreeDPrompt}". This might take a few minutes...`,
});

try {
const result = await generate3D(ThreeDPrompt, runtime);

if (result.success && result.url && result.file_name) {
// Download the 3D file
const response = await fetch(result.url);
const arrayBuffer = await response.arrayBuffer();
const ThreeDFileName = `content_cache/generated_3d_${result.file_name}`;

// ensure the directory is existed
const directoryPath = path.dirname(ThreeDFileName);
if (!fs.existsSync(directoryPath)) {
fs.mkdirSync(directoryPath, { recursive: true });
}

// Save 3D file
fs.writeFileSync(ThreeDFileName, Buffer.from(arrayBuffer));

callback(
{
text: "Here's your generated 3D object!",
attachments: [
{
id: crypto.randomUUID(),
url: result.url,
title: "Generated 3D",
source: "ThreeDGeneration",
description: ThreeDPrompt,
text: ThreeDPrompt,
},
],
},
[ThreeDFileName]
); // Add the 3D file to the attachments
} else {
callback({
text: `3D generation failed: ${result.error}`,
error: true,
});
}
} catch (error) {
elizaLogger.error(`Failed to generate 3D. Error: ${error}`);
callback({
text: `3D generation failed: ${error.message}`,
error: true,
});
}
},
examples: [
[
{
user: "{{user1}}",
content: { text: "Generate a 3D object of a cat playing piano" },
},
{
user: "{{agentName}}",
content: {
text: "I'll create a 3D object of a cat playing piano for you",
action: "GENERATE_3D",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Can you make a 3D object of a anime character Goku?",
},
},
{
user: "{{agentName}}",
content: {
text: "I'll generate a 3D object of a anime character Goku for you",
action: "GENERATE_3D",
},
},
],
],
} as Action;

export const ThreeDGenerationPlugin: Plugin = {
name: "3DGeneration",
description: "Generate 3D using Hyper 3D",
actions: [ThreeDGeneration],
evaluators: [],
providers: [],
};
15 changes: 15 additions & 0 deletions packages/plugin-3d-generation/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": [
"node"
]
},
"include": [
"src/**/*.ts"
]
}
21 changes: 21 additions & 0 deletions packages/plugin-3d-generation/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
sourcemap: true,
clean: true,
format: ["esm"],
external: [
"dotenv",
"fs",
"path",
"process",
"@reflink/reflink",
"@node-llama-cpp",
"@fal-ai/client",
"https",
"http",
"agentkeepalive",
],
});
Loading