+
- {/*
-
-
*/}
-
-
{title}
+
+
{icon}
+
+ {title}
+
{description}
@@ -63,9 +69,11 @@ export default function HomepageFeatures() {
- {FeatureList.map((props, idx) => (
-
- ))}
+
+ {FeatureList.map((props, idx) => (
+
+ ))}
+
diff --git a/docs/src/components/HomepageFeatures/styles.module.css b/docs/src/components/HomepageFeatures/styles.module.css
index b248eb2e5d..8c7ff50f7f 100644
--- a/docs/src/components/HomepageFeatures/styles.module.css
+++ b/docs/src/components/HomepageFeatures/styles.module.css
@@ -3,9 +3,57 @@
align-items: center;
padding: 2rem 0;
width: 100%;
+ flex-wrap: wrap;
}
.featureSvg {
height: 200px;
width: 200px;
}
+
+.featureIcon {
+ height: 100px;
+ width: 100px;
+ font-size: 2rem;
+}
+
+.featureGrid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1rem;
+}
+
+@media (max-width: 768px) {
+ .featureGrid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0rem;
+ }
+
+ .featureSvg {
+ height: 150px;
+ width: 150px;
+ }
+
+ .featureIcon {
+ height: 80px;
+ width: 80px;
+ font-size: 1.5rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .featureGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .featureSvg {
+ height: 100px;
+ width: 100px;
+ }
+
+ .featureIcon {
+ height: 60px;
+ width: 60px;
+ font-size: 1.2rem;
+ }
+}
diff --git a/docs/src/components/HomepageHeader/index.js b/docs/src/components/HomepageHeader/index.js
index b6759bc6f8..969e2108fe 100644
--- a/docs/src/components/HomepageHeader/index.js
+++ b/docs/src/components/HomepageHeader/index.js
@@ -44,7 +44,7 @@ function HomepageHeader() {
alt="blurred"
/>
- {`npm install eliza`}
+ {`npm package coming soon`}
diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css
index ab05cc5c01..af54dbd776 100644
--- a/docs/src/css/custom.css
+++ b/docs/src/css/custom.css
@@ -6,7 +6,7 @@
/* You can override the default Infima variables here. */
:root {
- --ifm-color-primary: darkblue;
+ --ifm-color-primary: #ffa600;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
--ifm-color-primary-darkest: #205d3b;
@@ -27,4 +27,27 @@
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
+ --ifm-footer-background-color: #ffa600;
+}
+
+.footer {
+ background-color: #242736;
+}
+
+.button--primary {
+ background: linear-gradient(
+ 180deg,
+ var(--token-e94f1f99-3833-4c15-8d11-a67e80285705, rgb(249, 140, 19))
+ /* {"name":"Orange"} */ 0%,
+ rgb(255, 166, 0) 100%
+ );
+ border: none;
+ padding: 1rem 2rem;
+ font-size: 1.2rem;
+ transition: background 0.3s;
+ color: white;
+}
+
+.button--primary:hover {
+ background: linear-gradient(rgb(255, 156, 43) 0%, rgb(255, 166, 0) 100%);
}
diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js
index ce448e0a0b..78ce7d5972 100644
--- a/docs/src/pages/index.js
+++ b/docs/src/pages/index.js
@@ -2,38 +2,13 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import HomepageFeatures from "@site/src/components/HomepageFeatures";
import Layout from "@theme/Layout";
-import Heading from "@theme/Heading";
import styles from "./index.module.css";
-
-function HomepageHeader() {
- const { siteConfig } = useDocusaurusContext();
- return (
-
-
-
- {siteConfig.title}
-
-
{siteConfig.tagline}
-
-
- );
-}
+import HomepageHeader from "../components/HomepageHeader";
export default function Home() {
const { siteConfig } = useDocusaurusContext();
return (
-
diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico
index cc99810a03..7bc84ea064 100644
Binary files a/docs/static/img/favicon.ico and b/docs/static/img/favicon.ico differ
diff --git a/package.json b/package.json
index 282297c4c2..808745a33b 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
}
},
"engines": {
- "node": ">=22"
+ "node": ">=23"
},
"dependencies": {
"ollama-ai-provider": "^0.16.1",
diff --git a/packages/agent/src/index.ts b/packages/agent/src/index.ts
index 221b7d93e6..3c773fc29e 100644
--- a/packages/agent/src/index.ts
+++ b/packages/agent/src/index.ts
@@ -306,7 +306,6 @@ const rl = readline.createInterface({
});
async function handleUserInput(input, agentId) {
- console.log("handleUserInput", input, agentId);
if (input.toLowerCase() === "exit") {
rl.close();
return;
diff --git a/packages/client-direct/src/index.ts b/packages/client-direct/src/index.ts
index 66376a576c..2f28f05bdb 100644
--- a/packages/client-direct/src/index.ts
+++ b/packages/client-direct/src/index.ts
@@ -129,7 +129,6 @@ export class DirectClient {
this.app.post(
"/:agentId/message",
async (req: express.Request, res: express.Response) => {
- console.log("DirectClient message");
const agentId = req.params.agentId;
const roomId = stringToUuid(
req.body.roomId ?? "default-room-" + agentId
diff --git a/packages/client-discord/src/actions/download_media.ts b/packages/client-discord/src/actions/download_media.ts
index 90edac1737..48a69117cc 100644
--- a/packages/client-discord/src/actions/download_media.ts
+++ b/packages/client-discord/src/actions/download_media.ts
@@ -10,6 +10,7 @@ import {
IVideoService,
Memory,
ModelClass,
+ Service,
ServiceType,
State,
} from "@ai16z/eliza/src/types.ts";
@@ -85,9 +86,9 @@ export default {
options: any,
callback: HandlerCallback
) => {
- const videoService = runtime.getService(
- ServiceType.VIDEO
- );
+ const videoService = runtime
+ .getService(ServiceType.VIDEO)
+ .getInstance();
if (!state) {
state = (await runtime.composeState(message)) as State;
}
diff --git a/packages/client-discord/src/attachments.ts b/packages/client-discord/src/attachments.ts
index 4b6e7ba5e5..15c03164bb 100644
--- a/packages/client-discord/src/attachments.ts
+++ b/packages/client-discord/src/attachments.ts
@@ -8,6 +8,7 @@ import {
IVideoService,
Media,
ModelClass,
+ Service,
ServiceType,
} from "@ai16z/eliza/src/types.ts";
import { Attachment, Collection } from "discord.js";
@@ -102,8 +103,8 @@ export class AttachmentManager {
media = await this.processImageAttachment(attachment);
} else if (
attachment.contentType?.startsWith("video/") ||
- this.runtime
- .getService(ServiceType.VIDEO)
+ this.runtime.getService(ServiceType.VIDEO)
+ .getInstance()
.isVideoUrl(attachment.url)
) {
media = await this.processVideoAttachment(attachment);
@@ -136,7 +137,8 @@ export class AttachmentManager {
}
const transcription = await this.runtime
- .getService(ServiceType.TRANSCRIPTION)
+ .getService(ServiceType.TRANSCRIPTION)
+ .getInstance()
.transcribeAttachment(audioBuffer);
const { title, description } = await generateSummary(
this.runtime,
@@ -217,7 +219,8 @@ export class AttachmentManager {
const response = await fetch(attachment.url);
const pdfBuffer = await response.arrayBuffer();
const text = await this.runtime
- .getService(ServiceType.PDF)
+ .getService(ServiceType.PDF)
+ .getInstance()
.convertPdfToText(Buffer.from(pdfBuffer));
const { title, description } = await generateSummary(
this.runtime,
@@ -285,9 +288,8 @@ export class AttachmentManager {
): Promise {
try {
const { description, title } = await this.runtime
- .getService(
- ServiceType.IMAGE_DESCRIPTION
- )
+ .getService(ServiceType.IMAGE_DESCRIPTION)
+ .getInstance()
.describeImage(attachment.url);
return {
id: attachment.id,
@@ -321,11 +323,13 @@ export class AttachmentManager {
): Promise {
if (
this.runtime
- .getService(ServiceType.VIDEO)
+ .getService(ServiceType.VIDEO)
+ .getInstance()
.isVideoUrl(attachment.url)
) {
const videoInfo = await this.runtime
- .getService(ServiceType.VIDEO)
+ .getService(ServiceType.VIDEO)
+ .getInstance()
.processVideo(attachment.url);
return {
id: attachment.id,
diff --git a/packages/client-discord/src/index.ts b/packages/client-discord/src/index.ts
index eda359bae0..f1577f21f9 100644
--- a/packages/client-discord/src/index.ts
+++ b/packages/client-discord/src/index.ts
@@ -73,6 +73,7 @@ export class DiscordClient extends EventEmitter {
this.runtime.registerAction(transcribe_media);
this.runtime.registerAction(download_media);
+
this.runtime.providers.push(channelStateProvider);
this.runtime.providers.push(voiceStateProvider);
}
diff --git a/packages/client-discord/src/messages.ts b/packages/client-discord/src/messages.ts
index 349f57160b..4d233f658d 100644
--- a/packages/client-discord/src/messages.ts
+++ b/packages/client-discord/src/messages.ts
@@ -36,6 +36,7 @@ import {
import { elizaLogger } from "@ai16z/eliza/src/logger.ts";
import { AttachmentManager } from "./attachments.ts";
import { VoiceManager } from "./voice.ts";
+import { Service } from "@ai16z/eliza";
const MAX_MESSAGE_LENGTH = 1900;
async function generateSummary(
@@ -340,7 +341,7 @@ export class MessageManager {
if (
message.interaction ||
message.author.id ===
- this.client.user?.id /* || message.author?.bot*/
+ this.client.user?.id /* || message.author?.bot*/
)
return;
const userId = message.author.id as UUID;
@@ -388,10 +389,10 @@ export class MessageManager {
url: message.url,
inReplyTo: message.reference?.messageId
? stringToUuid(
- message.reference.messageId +
- "-" +
- this.runtime.agentId
- )
+ message.reference.messageId +
+ "-" +
+ this.runtime.agentId
+ )
: undefined,
};
@@ -502,10 +503,11 @@ export class MessageManager {
}
if (message.channel.type === ChannelType.GuildVoice) {
// For voice channels, use text-to-speech
- const audioStream = await this.runtime
- .getService(
+ const audioStream = await (
+ this.runtime.getService(
ServiceType.SPEECH_GENERATION
)
+ ).getInstance()
.generate(this.runtime, content.text);
await this.voiceManager.playAudioStream(
userId,
@@ -592,7 +594,8 @@ export class MessageManager {
// For voice channels, use text-to-speech for the error message
const errorMessage = "Sorry, I had a glitch. What was that?";
const audioStream = await this.runtime
- .getService(ServiceType.SPEECH_GENERATION)
+ .getService(ServiceType.SPEECH_GENERATION)
+ .getInstance()
.generate(this.runtime, errorMessage);
await this.voiceManager.playAudioStream(userId, audioStream);
} else {
@@ -656,13 +659,14 @@ export class MessageManager {
for (const url of urls) {
if (
- this.runtime
- .getService(ServiceType.VIDEO)
+ this.runtime.getService(ServiceType.VIDEO)
+ .getInstance()
.isVideoUrl(url)
) {
- const videoInfo = await this.runtime
- .getService(ServiceType.VIDEO)
- .processVideo(url);
+ const videoInfo = await (this.runtime
+ .getService(ServiceType.VIDEO)
+ .getInstance()
+ .processVideo(url));
attachments.push({
id: `youtube-${Date.now()}`,
url: url,
@@ -673,7 +677,8 @@ export class MessageManager {
});
} else {
const { title, bodyContent } = await this.runtime
- .getService(ServiceType.BROWSER)
+ .getService(ServiceType.BROWSER)
+ .getInstance()
.getPageContent(url, this.runtime);
const { title: newTitle, description } = await generateSummary(
this.runtime,
diff --git a/packages/client-discord/src/voice.ts b/packages/client-discord/src/voice.ts
index 6098592592..744a8106b8 100644
--- a/packages/client-discord/src/voice.ts
+++ b/packages/client-discord/src/voice.ts
@@ -31,6 +31,7 @@ import {
ITranscriptionService,
Memory,
ModelClass,
+ Service,
ServiceType,
State,
UUID,
@@ -399,9 +400,8 @@ export class VoiceManager extends EventEmitter {
console.log("starting transcription");
const text = await this.runtime
- .getService(
- ServiceType.TRANSCRIPTION
- )
+ .getService(ServiceType.TRANSCRIPTION)
+ .getInstance()
.transcribe(wavBuffer);
console.log("transcribed text: ", text);
transcriptionText += text;
@@ -541,9 +541,8 @@ export class VoiceManager extends EventEmitter {
state
);
const responseStream = await this.runtime
- .getService(
- ServiceType.SPEECH_GENERATION
- )
+ .getService(ServiceType.SPEECH_GENERATION)
+ .getInstance()
.generate(this.runtime, content.text);
if (responseStream) {
diff --git a/packages/client-twitter/src/base.ts b/packages/client-twitter/src/base.ts
index 9279816ea7..12adf70eda 100644
--- a/packages/client-twitter/src/base.ts
+++ b/packages/client-twitter/src/base.ts
@@ -218,7 +218,8 @@ export class ClientBase extends EventEmitter {
await this.twitterClient.login(
this.runtime.getSetting("TWITTER_USERNAME"),
this.runtime.getSetting("TWITTER_PASSWORD"),
- this.runtime.getSetting("TWITTER_EMAIL")
+ this.runtime.getSetting("TWITTER_EMAIL"),
+ this.runtime.getSetting("TWITTER_2FA_SECRET")
);
console.log("Logged in to Twitter");
const cookies = await this.twitterClient.getCookies();
@@ -240,7 +241,8 @@ export class ClientBase extends EventEmitter {
await this.twitterClient.login(
this.runtime.getSetting("TWITTER_USERNAME"),
this.runtime.getSetting("TWITTER_PASSWORD"),
- this.runtime.getSetting("TWITTER_EMAIL")
+ this.runtime.getSetting("TWITTER_EMAIL"),
+ this.runtime.getSetting("TWITTER_2FA_SECRET")
);
const cookies = await this.twitterClient.getCookies();
@@ -258,7 +260,7 @@ export class ClientBase extends EventEmitter {
await new Promise((resolve) => setTimeout(resolve, 10000));
try {
return await this.twitterClient.getUserIdByScreenName(
- this.runtime.getSetting("TWITTER_USERNAME")
+ this.runtime.getSetting("TWITTER_USERNAME"),
);
} catch (error) {
console.error("Error getting user ID:", error);
diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts
index 055aae2de3..d4e9421855 100644
--- a/packages/client-twitter/src/post.ts
+++ b/packages/client-twitter/src/post.ts
@@ -33,8 +33,8 @@ export class TwitterPostClient extends ClientBase {
this.generateNewTweet();
setTimeout(
generateNewTweetLoop,
- (Math.floor(Math.random() * (20 - 2 + 1)) + 2) * 60 * 1000
- ); // Random interval between 4-8 hours
+ (Math.floor(Math.random() * (4 - 1 + 1)) + 1) * 60 * 60 * 1000
+ ); // Random interval between 1 and 4 hours
};
// setTimeout(() => {
generateNewTweetLoop();
diff --git a/packages/client-twitter/src/search.ts b/packages/client-twitter/src/search.ts
index 8ac4c88bf1..0a38793200 100644
--- a/packages/client-twitter/src/search.ts
+++ b/packages/client-twitter/src/search.ts
@@ -237,9 +237,8 @@ export class TwitterSearchClient extends ClientBase {
const imageDescriptions = [];
for (const photo of selectedTweet.photos) {
const description = await this.runtime
- .getService(
- ServiceType.IMAGE_DESCRIPTION
- )
+ .getService(ServiceType.IMAGE_DESCRIPTION)
+ .getInstance()
.describeImage(photo.url);
imageDescriptions.push(description);
}
diff --git a/packages/core/src/embedding.ts b/packages/core/src/embedding.ts
index 13bd64f508..e212558875 100644
--- a/packages/core/src/embedding.ts
+++ b/packages/core/src/embedding.ts
@@ -97,8 +97,6 @@ export async function embed(runtime: IAgentRuntime, input: string) {
throw new Error("No embedding model configured");
}
- console.log("embeddingModel", embeddingModel);
-
// Try local embedding first
if (
runtime.character.modelProvider !== ModelProviderName.OPENAI &&
diff --git a/packages/core/src/generation.ts b/packages/core/src/generation.ts
index dc01e535e1..07909847b2 100644
--- a/packages/core/src/generation.ts
+++ b/packages/core/src/generation.ts
@@ -2,7 +2,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
import { createGroq } from "@ai-sdk/groq";
import { createOpenAI } from "@ai-sdk/openai";
import { getModel } from "./models.ts";
-import { IImageDescriptionService, ModelClass } from "./types.ts";
+import { IImageDescriptionService, ModelClass, Service } from "./types.ts";
import { generateText as aiGenerateText } from "ai";
import { Buffer } from "buffer";
import { createOllama } from "ollama-ai-provider";
@@ -199,9 +199,8 @@ export async function generateText({
"Using local Llama model for text completion."
);
response = await runtime
- .getService(
- ServiceType.TEXT_GENERATION
- )
+ .getService(ServiceType.TEXT_GENERATION)
+ .getInstance()
.queueTextCompletion(
context,
temperature,
@@ -741,7 +740,8 @@ export const generateCaption = async (
}> => {
const { imageUrl } = data;
const resp = await runtime
- .getService(ServiceType.IMAGE_DESCRIPTION)
+ .getService(ServiceType.IMAGE_DESCRIPTION)
+ .getInstance()
.describeImage(imageUrl);
return {
title: resp.title.trim(),
diff --git a/packages/core/src/memory.ts b/packages/core/src/memory.ts
index 298c17543d..6187e536a7 100644
--- a/packages/core/src/memory.ts
+++ b/packages/core/src/memory.ts
@@ -159,8 +159,6 @@ export class MemoryManager implements IMemoryManager {
const existingMessage =
await this.runtime.databaseAdapter.getMemoryById(memory.id);
- console.log("existingMessage", existingMessage);
-
if (existingMessage) {
console.log("Memory already exists, skipping");
return;
diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts
index 0beb43a848..5e5fd5891d 100644
--- a/packages/core/src/runtime.ts
+++ b/packages/core/src/runtime.ts
@@ -150,13 +150,13 @@ export class AgentRuntime implements IAgentRuntime {
return this.memoryManagers.get(tableName) || null;
}
- getService(service: ServiceType): T | null {
+ getService(service: ServiceType): typeof Service | null {
const serviceInstance = this.services.get(service);
if (!serviceInstance) {
console.error(`Service ${service} not found`);
return null;
}
- return serviceInstance as T;
+ return serviceInstance as typeof Service;
}
registerService(service: Service): void {
const serviceType = (service as typeof Service).serviceType;
@@ -499,7 +499,6 @@ export class AgentRuntime implements IAgentRuntime {
* @returns The results of the evaluation.
*/
async evaluate(message: Memory, state?: State, didRespond?: boolean) {
- console.log("Evaluate: ", didRespond);
const evaluatorPromises = this.evaluators.map(
async (evaluator: Evaluator) => {
console.log("Evaluating", evaluator.name);
@@ -897,8 +896,6 @@ Text: ${attachment.text}
async function getKnowledge(runtime: AgentRuntime, message: Memory): Promise {
const embedding = await embed(runtime, message.content.text);
- console.log("message.agentId", message.agentId)
-
const memories = await runtime.knowledgeManager.searchMemoriesByEmbedding(
embedding,
{
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 7d61a241f5..040b743a68 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -536,7 +536,7 @@ export interface IAgentRuntime {
getMemoryManager(name: string): IMemoryManager | null;
- getService(service: string): Service | null;
+ getService(service: string): typeof Service | null;
registerService(service: Service): void;
diff --git a/packages/plugin-bootstrap/src/providers/facts.ts b/packages/plugin-bootstrap/src/providers/facts.ts
index e335f1baf7..241e581854 100644
--- a/packages/plugin-bootstrap/src/providers/facts.ts
+++ b/packages/plugin-bootstrap/src/providers/facts.ts
@@ -20,9 +20,6 @@ const factsProvider: Provider = {
const embedding = await embed(runtime, recentMessages);
- console.log("embedding", embedding);
- console.log("embedding length", embedding.length);
-
const memoryManager = new MemoryManager({
runtime,
tableName: "facts",
diff --git a/packages/plugin-image-generation/src/index.ts b/packages/plugin-image-generation/src/index.ts
index 22aa577ed8..e05c779832 100644
--- a/packages/plugin-image-generation/src/index.ts
+++ b/packages/plugin-image-generation/src/index.ts
@@ -9,9 +9,34 @@ import {
} from "@ai16z/eliza/src/types.ts";
import { generateCaption, generateImage } from "@ai16z/eliza/src/generation.ts";
+import fs from 'fs';
+import path from 'path';
+
+export function saveBase64Image(base64Data: string, filename: string): string {
+ // Create generatedImages directory if it doesn't exist
+ const imageDir = path.join(process.cwd(), 'generatedImages');
+ if (!fs.existsSync(imageDir)) {
+ fs.mkdirSync(imageDir, { recursive: true });
+ }
+
+ // Remove the data:image/png;base64 prefix if it exists
+ const base64Image = base64Data.replace(/^data:image\/\w+;base64,/, '');
+
+ // Create a buffer from the base64 string
+ const imageBuffer = Buffer.from(base64Image, 'base64');
+
+ // Create full file path
+ const filepath = path.join(imageDir, `${filename}.png`);
+
+ // Save the file
+ fs.writeFileSync(filepath, imageBuffer);
+
+ return filepath;
+}
+
const imageGeneration: Action = {
name: "GENERATE_IMAGE",
- similes: ["IMAGE_GENERATION", "IMAGE_GEN", "CREATE_IMAGE", "MAKE_PICTURE"],
+ similes: ["IMAGE_GENERATION", "IMAGE_GEN", "CREATE_IMAGE", "MAKE_PICTURE",],
description: "Generate an image to go along with the message.",
validate: async (runtime: IAgentRuntime, message: Memory) => {
const anthropicApiKeyOk = !!runtime.getSetting("ANTHROPIC_API_KEY");
@@ -58,36 +83,62 @@ const imageGeneration: Action = {
);
for (let i = 0; i < images.data.length; i++) {
const image = images.data[i];
- elizaLogger.log(`Processing image ${i + 1}:`, image);
+
+ const base64Image = images.data[i];
+ // Save the image and get filepath
+ const filename = `generated_${Date.now()}_${i}`;
+ const filepath = saveBase64Image(base64Image, filename);
+ elizaLogger.log(`Processing image ${i + 1}:`, filename);
- const caption = await generateCaption(
+ //just dont even add a caption or a description just have it generate & send
+ /*
+ try {
+ const imageService = runtime.getService(ServiceType.IMAGE_DESCRIPTION);
+ if (imageService && typeof imageService.describeImage === 'function') {
+ const caption = await imageService.describeImage({ imageUrl: filepath });
+ captionText = caption.description;
+ captionTitle = caption.title;
+ }
+ } catch (error) {
+ elizaLogger.error("Caption generation failed, using default caption:", error);
+ }*/
+
+ const caption = "...";
+ /*= await generateCaption(
{
imageUrl: image,
},
runtime
- );
+ );*/
+
+ res.push({ image: filepath, caption: "..."});//caption.title });
elizaLogger.log(
`Generated caption for image ${i + 1}:`,
- caption.title
+ "..."//caption.title
);
- res.push({ image: image, caption: caption.title });
+ //res.push({ image: image, caption: caption.title });
callback(
{
- text: caption.description,
+ text: "...",//caption.description,
attachments: [
{
id: crypto.randomUUID(),
- url: image,
+ url: filepath,
title: "Generated image",
source: "imageGeneration",
- description: caption.title,
- text: caption.description,
+ description: "...",//caption.title,
+ text: "...",//caption.description,
},
],
},
- []
+ [
+ {
+ attachment: filepath,
+ name: `${filename}.png`
+ }
+ ]
);
}
} else {
diff --git a/packages/plugin-node/src/services/video.ts b/packages/plugin-node/src/services/video.ts
index 6e1ef6fe46..6aa1b91e52 100644
--- a/packages/plugin-node/src/services/video.ts
+++ b/packages/plugin-node/src/services/video.ts
@@ -328,7 +328,8 @@ export class VideoService extends Service {
console.log("Starting transcription...");
const startTime = Date.now();
const transcript = await runtime
- .getService(ServiceType.TRANSCRIPTION)
+ .getService(ServiceType.TRANSCRIPTION)
+ .getInstance()
.transcribe(audioBuffer);
const endTime = Date.now();
console.log(
diff --git a/packages/plugin-solana/src/actions/pumpfun.ts b/packages/plugin-solana/src/actions/pumpfun.ts
index a6453cbc63..ab49821ee0 100644
--- a/packages/plugin-solana/src/actions/pumpfun.ts
+++ b/packages/plugin-solana/src/actions/pumpfun.ts
@@ -1,5 +1,6 @@
import { AnchorProvider } from "@coral-xyz/anchor";
import { Wallet } from "@coral-xyz/anchor";
+import { generateImage } from "@ai16z/eliza/src/generation.ts";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import {
CreateTokenMetadata,
@@ -14,40 +15,53 @@ import settings from "@ai16z/eliza/src/settings.ts";
import {
ActionExample,
Content,
+ HandlerCallback,
IAgentRuntime,
Memory,
+ ModelClass,
+ State,
type Action,
} from "@ai16z/eliza/src/types.ts";
+import { composeContext } from "@ai16z/eliza/src/context.ts";
+import { generateObject } from "@ai16z/eliza/src/generation.ts";
+
+import {
+ walletProvider,
+ //WalletProvider,
+} from "../providers/wallet.ts";
+
+import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes/index.js";
+
export interface CreateAndBuyContent extends Content {
- deployerPrivateKey: string;
- tokenMetadata: CreateTokenMetadata;
- buyAmountSol: string | number;
- priorityFee: {
- unitLimit: number;
- unitPrice: number;
+ tokenMetadata: {
+ name: string;
+ symbol: string;
+ description: string;
+ image_description: string;
};
- allowOffCurve: boolean;
+ buyAmountSol: string | number;
}
+
export function isCreateAndBuyContent(
runtime: IAgentRuntime,
content: any
): content is CreateAndBuyContent {
+ console.log("Content for create & buy", content)
return (
- typeof content.deployerPrivateKey === "string" &&
typeof content.tokenMetadata === "object" &&
content.tokenMetadata !== null &&
+ typeof content.tokenMetadata.name === "string" &&
+ typeof content.tokenMetadata.symbol === "string" &&
+ typeof content.tokenMetadata.description === "string" &&
+ typeof content.tokenMetadata.image_description === "string" &&
(typeof content.buyAmountSol === "string" ||
- typeof content.buyAmountSol === "number") &&
- typeof content.priorityFee === "object" &&
- content.priorityFee !== null &&
- typeof content.priorityFee.unitLimit === "number" &&
- typeof content.priorityFee.unitPrice === "number" &&
- typeof content.allowOffCurve === "boolean"
+ typeof content.buyAmountSol === "number")
);
}
+
export const createAndBuyToken = async ({
deployer,
mint,
@@ -88,6 +102,9 @@ export const createAndBuyToken = async ({
priorityFee,
commitment
);
+
+ console.log("Create Results: ", createResults);
+
if (createResults.success) {
console.log(
"Success:",
@@ -111,9 +128,21 @@ export const createAndBuyToken = async ({
} else {
console.log(`${deployer.publicKey.toBase58()}:`, amount);
}
+
+ return {
+ success: true,
+ ca: mint.publicKey.toBase58(),
+ creator: deployer.publicKey.toBase58()
+ };
+
} else {
console.log("Create and Buy failed");
- }
+ return {
+ success: false,
+ ca: mint.publicKey.toBase58(),
+ error: createResults.error || "Transaction failed"
+ };
+ }
};
export const buyToken = async ({
@@ -213,6 +242,7 @@ export const sellToken = async ({
};
const promptConfirmation = async (): Promise => {
+ return true;
if (typeof window !== "undefined" && typeof window.confirm === "function") {
return window.confirm(
"Confirm the creation and purchase of the token?"
@@ -221,76 +251,247 @@ const promptConfirmation = async (): Promise => {
return true;
};
+// Save the base64 data to a file
+import * as fs from 'fs';
+import * as path from 'path';
+
+
+const pumpfunTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
+
+Example response:
+\`\`\`json
+{
+ "tokenMetadata": {
+ "name": "Test Token",
+ "symbol": "TEST",
+ "description": "A test token",
+ "image_description": "create an image of a rabbit"
+ },
+ "buyAmountSol": "0.00069"
+}
+\`\`\`
+
+{{recentMessages}}
+
+Given the recent messages, extract or generate (come up with if not included) the following information about the requested token creation:
+- Token name
+- Token symbol
+- Token description
+- Token image description
+- Amount of SOL to buy
+
+Respond with a JSON markdown block containing only the extracted values.`;
+
+
+
export default {
name: "CREATE_AND_BUY_TOKEN",
similes: ["CREATE_AND_PURCHASE_TOKEN", "DEPLOY_AND_BUY_TOKEN"],
validate: async (runtime: IAgentRuntime, message: Memory) => {
- return isCreateAndBuyContent(runtime, message.content);
+
+ return true;//return isCreateAndBuyContent(runtime, message.content);
},
description:
"Create a new token and buy a specified amount using SOL. Requires deployer private key, token metadata, buy amount in SOL, priority fee, and allowOffCurve flag.",
- handler: async (
- runtime: IAgentRuntime,
- message: Memory
- ): Promise => {
- const content = message.content;
- if (!isCreateAndBuyContent(runtime, content)) {
- console.error("Invalid content for CREATE_AND_BUY_TOKEN action.");
- return false;
- }
- const {
- deployerPrivateKey,
- tokenMetadata,
- buyAmountSol,
- priorityFee,
- allowOffCurve,
- } = content;
-
- const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY")!;
- const wallet = new Wallet(
- Keypair.fromSecretKey(new Uint8Array(JSON.parse(privateKey)))
- );
- const connection = new Connection(settings.RPC_URL!);
- const provider = new AnchorProvider(connection, wallet, {
- commitment: "finalized",
- });
- const sdk = new PumpFunSDK(provider);
- const slippage = runtime.getSetting("SLIPPAGE");
-
- try {
- const deployerKeypair = Keypair.fromSecretKey(
- Uint8Array.from(Buffer.from(deployerPrivateKey, "base64"))
- );
+ handler: async (
+ runtime: IAgentRuntime,
+ message: Memory,
+ state: State,
+ _options: { [key: string]: unknown },
+ callback?: HandlerCallback
+ ): Promise => {
+ console.log("Starting CREATE_AND_BUY_TOKEN handler...");
+
+ // Compose state if not provided
+ if (!state) {
+ state = (await runtime.composeState(message)) as State;
+ } else {
+ state = await runtime.updateRecentMessageState(state);
+ }
+
+ // Get wallet info for context
+ const walletInfo = await walletProvider.get(runtime, message, state);
+ state.walletInfo = walletInfo;
+
+ // Generate structured content from natural language
+ const pumpContext = composeContext({
+ state,
+ template: pumpfunTemplate,
+ });
+
+ const content = await generateObject({
+ runtime,
+ context: pumpContext,
+ modelClass: ModelClass.LARGE,
+ });
+
+ // Validate the generated content
+ if (!isCreateAndBuyContent(runtime, content)) {
+ console.error("Invalid content for CREATE_AND_BUY_TOKEN action.");
+ return false;
+ }
+
+ const { tokenMetadata, buyAmountSol } = content;
+ /*
+ // Generate image if tokenMetadata.file is empty or invalid
+ if (!tokenMetadata.file || tokenMetadata.file.length < 100) { // Basic validation
+ try {
+ const imageResult = await generateImage({
+ prompt: `logo for ${tokenMetadata.name} (${tokenMetadata.symbol}) token - ${tokenMetadata.description}`,
+ width: 512,
+ height: 512,
+ count: 1
+ }, runtime);
+
+ if (imageResult.success && imageResult.data && imageResult.data.length > 0) {
+ // Remove the "data:image/png;base64," prefix if present
+ tokenMetadata.file = imageResult.data[0].replace(/^data:image\/[a-z]+;base64,/, '');
+ } else {
+ console.error("Failed to generate image:", imageResult.error);
+ return false;
+ }
+ } catch (error) {
+ console.error("Error generating image:", error);
+ return false;
+ }
+ } */
- const mintKeypair = Keypair.generate();
+ const imageResult = await generateImage({
+ prompt: `logo for ${tokenMetadata.name} (${tokenMetadata.symbol}) token - ${tokenMetadata.description}`,
+ width: 256,
+ height: 256,
+ count: 1
+ }, runtime);
- const createAndBuyConfirmation = await promptConfirmation();
- if (!createAndBuyConfirmation) {
- console.log("Create and buy token canceled by user");
- return false;
+
+ tokenMetadata.image_description = imageResult.data[0].replace(/^data:image\/[a-z]+;base64,/, '');
+
+
+
+
+ // Convert base64 string to Blob
+ const base64Data = tokenMetadata.image_description;
+ const outputPath = path.join(process.cwd(), `generated_image_${Date.now()}.txt`);
+ fs.writeFileSync(outputPath, base64Data);
+ console.log(`Base64 data saved to: ${outputPath}`);
+
+
+ const byteCharacters = atob(base64Data);
+ const byteNumbers = new Array(byteCharacters.length);
+ for (let i = 0; i < byteCharacters.length; i++) {
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
}
+ const byteArray = new Uint8Array(byteNumbers);
+ const blob = new Blob([byteArray], { type: 'image/png' });
+
+ // Add the default decimals and convert file to Blob
+ const fullTokenMetadata: CreateTokenMetadata = {
+ name: tokenMetadata.name,
+ symbol: tokenMetadata.symbol,
+ description: tokenMetadata.description,
+ file: blob,
+ };
- // Execute Create and Buy
- await createAndBuyToken({
- deployer: deployerKeypair,
- mint: mintKeypair,
- tokenMetadata: tokenMetadata as CreateTokenMetadata,
- buyAmountSol: BigInt(buyAmountSol),
- priorityFee: priorityFee as PriorityFee,
- allowOffCurve: allowOffCurve as boolean,
- sdk,
- connection,
- slippage,
- });
+ // Default priority fee for high network load
+ const priorityFee = {
+ unitLimit: 100_000_000,
+ unitPrice: 100_000
+ };
+ const slippage = "2000"
+ try {
+ // Get private key from settings and create deployer keypair
+ const privateKeyString = runtime.getSetting("WALLET_PRIVATE_KEY")!;
+ const secretKey = bs58.decode(privateKeyString);
+ const deployerKeypair = Keypair.fromSecretKey(secretKey);
- console.log(
- `Token created and purchased successfully! View at: https://pump.fun/${mintKeypair.publicKey.toBase58()}`
- );
- return true;
- } catch (error) {
- console.error("Error during create and buy token:", error);
- return false;
- }
+ // Generate new mint keypair
+ const mintKeypair = Keypair.generate();
+ console.log(`Generated mint address: ${mintKeypair.publicKey.toBase58()}`);
+
+ // Setup connection and SDK
+ const connection = new Connection(settings.RPC_URL!, {
+ commitment: "confirmed",
+ confirmTransactionInitialTimeout: 500000, // 120 seconds
+ wsEndpoint: settings.RPC_URL!.replace('https', 'wss')
+ });
+
+ const wallet = new Wallet(deployerKeypair);
+ const provider = new AnchorProvider(connection, wallet, {
+ commitment: "finalized"
+ });
+ const sdk = new PumpFunSDK(provider);
+ // const slippage = runtime.getSetting("SLIPPAGE");
+
+
+ const createAndBuyConfirmation = await promptConfirmation();
+ if (!createAndBuyConfirmation) {
+ console.log("Create and buy token canceled by user");
+ return false;
+ }
+
+ // Convert SOL to lamports (1 SOL = 1_000_000_000 lamports)
+ const lamports = Math.floor(Number(buyAmountSol) * 1_000_000_000);
+
+ console.log("Executing create and buy transaction...");
+ const result = await createAndBuyToken({
+ deployer: deployerKeypair,
+ mint: mintKeypair,
+ tokenMetadata: fullTokenMetadata,
+ buyAmountSol: BigInt(lamports),
+ priorityFee,
+ allowOffCurve: false,
+ sdk,
+ connection,
+ slippage,
+ });
+
+
+ if (callback) {
+ if (result.success) {
+ callback({
+ text: `Token ${tokenMetadata.name} (${tokenMetadata.symbol}) created successfully!\nContract Address: ${result.ca}\nCreator: ${result.creator}\nView at: https://pump.fun/${result.ca}`,
+ content: {
+ tokenInfo: {
+ symbol: tokenMetadata.symbol,
+ address: result.ca,
+ creator: result.creator,
+ name: tokenMetadata.name,
+ description: tokenMetadata.description,
+ timestamp: Date.now()
+ }
+ }
+ });
+ } else {
+ callback({
+ text: `Failed to create token: ${result.error}\nAttempted mint address: ${result.ca}`,
+ content: {
+ error: result.error,
+ mintAddress: result.ca
+ }
+ });
+ }
+ }
+ //await trustScoreDb.addToken(tokenInfo);
+ /*
+ // Update runtime state
+ await runtime.updateState({
+ ...state,
+ lastCreatedToken: tokenInfo
+ });
+ */
+ // Log success message with token view URL
+ const successMessage = `Token created and purchased successfully! View at: https://pump.fun/${mintKeypair.publicKey.toBase58()}`;
+ console.log(successMessage);
+ return result.success;
+ } catch (error) {
+ if (callback) {
+ callback({
+ text: `Error during token creation: ${error.message}`,
+ content: { error: error.message }
+ });
+ }
+ return false;
+ }
},
examples: [
@@ -298,32 +499,27 @@ export default {
{
user: "{{user1}}",
content: {
- deployerPrivateKey: "Base64EncodedPrivateKey",
- tokenMetadata: {
- name: "MyToken",
- symbol: "MTK",
- description: "My first token",
- file: "Base64EncodedFile", // blob file of the image
- decimals: DEFAULT_DECIMALS,
- },
- buyAmountSol: "1000000000", // 1 SOL in lamports
- priorityFee: 1000,
- allowOffCurve: false,
- },
+ text: "Create a new token called GLITCHIZA with symbol GLITCHIZA and generate a description about it. Also come up with a description for it to use for image generation .buy 0.00069 SOL worth."
+ }
},
{
user: "{{user2}}",
content: {
- text: "Creating and buying 1 SOL worth of MyToken...",
+ text: "Token GLITCHIZA (GLITCHIZA) created successfully!\nContract Address: 3kD5DN4bbA3nykb1abjS66VF7cYZkKdirX8bZ6ShJjBB\nCreator: 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa\nView at: https://pump.fun/EugPwuZ8oUMWsYHeBGERWvELfLGFmA1taDtmY8uMeX6r",
action: "CREATE_AND_BUY_TOKEN",
- },
- },
- {
- user: "{{user2}}",
- content: {
- text: "Token created and purchased successfully! View at: https://pump.fun/MintPublicKey",
- },
- },
- ],
- ] as ActionExample[][],
+ content: {
+ tokenInfo: {
+ symbol: "GLITCHIZA",
+ address: "EugPwuZ8oUMWsYHeBGERWvELfLGFmA1taDtmY8uMeX6r",
+ creator: "9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa",
+ name: "GLITCHIZA",
+ description: "A GLITCHIZA token"
+ }
+ }
+ }
+ }
+ ]
+ ] as ActionExample[][]
+
+ ,
} as Action;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e55a04510a..d64fef99e6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10461,8 +10461,8 @@ packages:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
- streamx@2.20.1:
- resolution: {integrity: sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==}
+ streamx@2.20.2:
+ resolution: {integrity: sha512-aDGDLU+j9tJcUdPGOaHmVF1u/hhI+CsGkT02V3OKlHDV7IukOI+nTWAGkiZEKCO35rWN1wIr4tS7YFr1f4qSvA==}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
@@ -16797,7 +16797,7 @@ snapshots:
bare-stream@2.3.2:
dependencies:
- streamx: 2.20.1
+ streamx: 2.20.2
optional: true
base-x@3.0.10:
@@ -24231,7 +24231,7 @@ snapshots:
streamsearch@1.1.0: {}
- streamx@2.20.1:
+ streamx@2.20.2:
dependencies:
fast-fifo: 1.3.2
queue-tick: 1.0.1
@@ -24444,7 +24444,7 @@ snapshots:
dependencies:
b4a: 1.6.7
fast-fifo: 1.3.2
- streamx: 2.20.1
+ streamx: 2.20.2
tar@6.2.1:
dependencies:
diff --git a/scripts/build.sh b/scripts/build.sh
index e94f5d1e50..51292d99c2 100644
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -1,5 +1,14 @@
#!/bin/bash
+# Check Node.js version
+REQUIRED_NODE_VERSION=23
+CURRENT_NODE_VERSION=$(node -v | cut -d'.' -f1 | sed 's/v//')
+
+if (( CURRENT_NODE_VERSION < REQUIRED_NODE_VERSION )); then
+ echo "Error: Node.js version must be $REQUIRED_NODE_VERSION or higher. Current version is $CURRENT_NODE_VERSION."
+ exit 1
+fi
+
# Navigate to the script's directory
cd "$(dirname "$0")"/..