diff --git a/.env.example b/.env.example index 6407ffd8a3e..295dc3aaf0b 100644 --- a/.env.example +++ b/.env.example @@ -29,6 +29,11 @@ X_SERVER_URL= XAI_API_KEY= XAI_MODEL= +#POST INTERVAL RANDOM MIN-MAX MINUTES +POST_INTERVAL_MIN= #90 #Default +POST_INTERVAL_MAX= #180 #Default + + #USE IMAGE GEN IMAGE_GEN= #TRUE diff --git a/README.md b/README.md index 73d580d70f5..481507020b6 100644 --- a/README.md +++ b/README.md @@ -1,212 +1,100 @@ -# Eliza +# Eliza 🤖 -Eliza Banner +
+ Eliza Banner +
-### [For Chinese Version: 中文说明](./README_CN.md) +
+ + 📖 [Documentation](https://ai16z.github.io/eliza/) | 🎯 [Examples](https://github.com/thejoven/awesome-eliza) + +
-### [For Japanese Version: 日本語の説明](./README_JA.md) +## ✨ Features -### [For Korean Version: 한국어 설명](./README_KOR.md) - -### [For French Version: Instructions en français](./README_FR.md) - -### [For Portuguese Version: Instruções em português](./README_PTBR.md) - -## Features - -- 🛠 Full-featured Discord, Twitter and Telegram connectors +- 🛠️ Full-featured Discord, Twitter and Telegram connectors +- 🔗 Support for every model (Llama, Grok, OpenAI, Anthropic, etc.) - 👥 Multi-agent and room support - 📚 Easily ingest and interact with your documents - 💾 Retrievable memory and document store -- 🚀 Highly extensible - create your own actions and clients to extend capabilities -- ☁️ Supports many models, including local Llama, OpenAI, Anthropic, Groq, and more +- 🚀 Highly extensible - create your own actions and clients +- ☁️ Supports many models (local Llama, OpenAI, Anthropic, Groq, etc.) - 📦 Just works! -## What can I use it for? +## 🎯 Use Cases - 🤖 Chatbots - 🕵️ Autonomous Agents -- 📈 Business process handling -- 🎮 Video game NPCs - -# Getting Started - -**Prerequisites (MUST):** - -- [Python 2.7+](https://www.python.org/downloads/) -- [Node.js 23.1+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) -- [pnpm](https://pnpm.io/installation) - -### Edit the .env file - -- Copy .env.example to .env and fill in the appropriate values -- Edit the TWITTER environment variables to add your bot's username and password - -### Edit the character file - -- Check out the file `src/core/defaultCharacter.ts` - you can modify this -- You can also load characters with the `pnpm start --characters="path/to/your/character.json"` and run multiple bots at the same time. - -After setting up the .env file and character file, you can start the bot with the following command: - -``` -pnpm i -pnpm start -``` - -# Customising Eliza - -### Adding custom actions - -To avoid git clashes in the core directory, we recommend adding custom actions to a `custom_actions` directory and then adding them to the `elizaConfig.yaml` file. See the `elizaConfig.example.yaml` file for an example. - -## Running with different models - -### Run with Llama +- 📈 Business Process Handling +- 🎮 Video Game NPCs +- 🧠 Trading -You can run Llama 70B or 405B models by setting the `XAI_MODEL` environment variable to `meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo` or `meta-llama/Meta-Llama-3.1-405B-Instruct` +## 🌍 Translations -### Run with Grok +
+Available Languages -You can run Grok models by setting the `XAI_MODEL` environment variable to `grok-beta` +- [中文说明](./README_CN.md) +- [日本語の説明](./README_JA.md) +- [한국어 설명](./README_KOR.md) +- [Instructions en français](./README_FR.md) +- [Instruções em português](./README_PTBR.md) -### Run with OpenAI +
-You can run OpenAI models by setting the `XAI_MODEL` environment variable to `gpt-4o-mini` or `gpt-4o` +## 🚀 Quick Start -## Additional Requirements +### Prerequisites -You may need to install Sharp. If you see an error when starting up, try installing it with the following command: - -``` -pnpm install --include=optional sharp -``` - -# Environment Setup - -You will need to add environment variables to your .env file to connect to various platforms: - -``` -# Required environment variables -DISCORD_APPLICATION_ID= -DISCORD_API_TOKEN= # Bot token -OPENAI_API_KEY=sk-* # OpenAI API key, starting with sk- -ELEVENLABS_XI_API_KEY= # API key from elevenlabs -GOOGLE_GENERATIVE_AI_API_KEY= # Gemini API key - -# ELEVENLABS SETTINGS -ELEVENLABS_MODEL_ID=eleven_multilingual_v2 -ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAM -ELEVENLABS_VOICE_STABILITY=0.5 -ELEVENLABS_VOICE_SIMILARITY_BOOST=0.9 -ELEVENLABS_VOICE_STYLE=0.66 -ELEVENLABS_VOICE_USE_SPEAKER_BOOST=false -ELEVENLABS_OPTIMIZE_STREAMING_LATENCY=4 -ELEVENLABS_OUTPUT_FORMAT=pcm_16000 - -TWITTER_DRY_RUN=false -TWITTER_USERNAME= # Account username -TWITTER_PASSWORD= # Account password -TWITTER_EMAIL= # Account email -TWITTER_COOKIES= # Account cookies - -X_SERVER_URL= -XAI_API_KEY= -XAI_MODEL= - - -# For asking Claude stuff -ANTHROPIC_API_KEY= - -WALLET_PRIVATE_KEY=EXAMPLE_WALLET_PRIVATE_KEY -WALLET_PUBLIC_KEY=EXAMPLE_WALLET_PUBLIC_KEY - -BIRDEYE_API_KEY= - -SOL_ADDRESS=So11111111111111111111111111111111111111112 -SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com -HELIUS_API_KEY= - - -## Telegram -TELEGRAM_BOT_TOKEN= - -TOGETHER_API_KEY= -``` +- [Python 2.7+](https://www.python.org/downloads/) +- [Node.js 22+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) +- [pnpm](https://pnpm.io/installation) -# Local Inference Setup +> **Note for Windows Users:** WSL is required -### CUDA Setup +### Edit the .env file -If you have an NVIDIA GPU, you can install CUDA to speed up local inference dramatically. +Copy .env.example to .env and fill in the appropriate values ``` -pnpm install -npx --no node-llama-cpp source download --gpu cuda +cp .env.example .env ``` -Make sure that you've installed the CUDA Toolkit, including cuDNN and cuBLAS. - -### Running locally - -Add XAI_MODEL and set it to one of the above options from [Run with -Llama](#run-with-llama) - you can leave X_SERVER_URL and XAI_API_KEY blank, it -downloads the model from huggingface and queries it locally - -# Clients - -## Discord Bot +### Edit the character file -For help with setting up your Discord Bot, check out here: https://discordjs.guide/preparations/setting-up-a-bot-application.html +1. Open `src/core/defaultCharacter.ts` to modify the default character -# Development +2. To load custom characters: + - Use `pnpm start --characters="path/to/your/character.json"` + - Multiple character files can be loaded simultaneously -## Testing +### Start Eliza -To run the test suite, you must got into each package: +After setting up the .env file and character file, you can start the bot with the following command: ```bash -pnpm test # Run tests once -pnpm test:watch # Run tests in watch mode -``` - -For database-specific tests: +pnpm i +pnpm build +pnpm start -```bash -pnpm test:sqlite # Run tests with SQLite -pnpm test:sqljs # Run tests with SQL.js +# The project iterates fast, sometimes you need to clean the project if you are coming back to the project +pnpm clean ``` -Tests are written using Jest and can be found in `src/**/*.test.ts` files. The test environment is configured to: - -- Load environment variables from `.env.test` -- Use a 2-minute timeout for long-running tests -- Support ESM modules -- Run tests in sequence (--runInBand) +#### Additional Requirements -To create new tests, add a `.test.ts` file adjacent to the code you're testing. - -## Docker - -For development purposes, you can run the docker container with the following command: +You may need to install Sharp. If you see an error when starting up, try installing it with the following command: ``` -pnpm docker +pnpm install --include=optional sharp ``` -This will drop you into a shell inside the docker container where you can continue to configure the instance - -and then you can start it with `pnpm start` - - -## Community & contact - -* [GitHub Issues](https://github.com/ai16z/eliza/issues). Best for: bugs you encounter using Eliza, and feature proposals. -* [Discord](https://discord.gg/ai16z). Best for: sharing your applications and hanging out with the community. +### Community & contact +- [GitHub Issues](https://github.com/ai16z/eliza/issues). Best for: bugs you encounter using Eliza, and feature proposals. +- [Discord](https://discord.gg/ai16z). Best for: sharing your applications and hanging out with the community. -**Contributors** +## Contributors diff --git a/packages/client-twitter/src/base.ts b/packages/client-twitter/src/base.ts index a4b46f5d46b..ff19fdda941 100644 --- a/packages/client-twitter/src/base.ts +++ b/packages/client-twitter/src/base.ts @@ -274,6 +274,24 @@ export class ClientBase extends EventEmitter { console.log("Twitter user ID:", userId); this.twitterUserId = userId; + // Initialize Twitter profile + const profile = await this.initializeProfile(); + if (profile) { + console.log("Twitter profile initialized:", profile); + + // Store profile info for use in responses + this.runtime.character = { + ...this.runtime.character, + twitterProfile: { + username: profile.username, + screenName: profile.screenName, + bio: profile.bio, + nicknames: profile.nicknames + } + }; + } + + await this.populateTimeline(); this.onReady(); @@ -603,4 +621,34 @@ export class ClientBase extends EventEmitter { }); } } + + async initializeProfile() { + const username = this.runtime.getSetting("TWITTER_USERNAME"); + if (!username) { + console.error("Twitter username not configured"); + return; + } + + try { + const profile = await this.requestQueue.add(async () => { + const profile = await this.twitterClient.getProfile(username); + return { + username, + screenName: profile.name || this.runtime.character.name, + bio: profile.biography || typeof this.runtime.character.bio === 'string' ? this.runtime.character.bio as string : this.runtime.character.bio.length > 0 ? this.runtime.character.bio[0] : "", + nicknames: this.runtime.character.twitterProfile?.nicknames || [] + }; + }); + + return profile; + } catch (error) { + console.error("Error fetching Twitter profile:", error); + return { + username: this.runtime.character.name, + screenName: username, + bio: typeof this.runtime.character.bio === 'string' ? this.runtime.character.bio as string : this.runtime.character.bio.length > 0 ? this.runtime.character.bio[0] : "", + nicknames: this.runtime.character.twitterProfile?.nicknames || [] + }; + } + } } diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index a999dded379..2bddb14ff14 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -28,19 +28,32 @@ Write a single sentence post that is {{adjective}} about {{topic}} (without ment Your response should not contain any questions. Brief, concise statements only. No emojis. Use \\n\\n (double spaces) between statements.`; export class TwitterPostClient extends ClientBase { - onReady() { + + + onReady(postImmediately: boolean = true) { const generateNewTweetLoop = () => { - this.generateNewTweet(); + const minMinutes = parseInt(this.runtime.getSetting("POST_INTERVAL_MIN")) || 90; + const maxMinutes = parseInt(this.runtime.getSetting("POST_INTERVAL_MAX")) || 180; + const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes; + const delay = randomMinutes * 60 * 1000; + setTimeout( - generateNewTweetLoop, - (Math.floor(Math.random() * (180 - 90 + 1)) + 90) * 60 * 1000 - ); // Random interval: min 90min/max 180min (1.5-3h), Results in min 8/max 16 posts per day + () => { + this.generateNewTweet(); + generateNewTweetLoop(); // Set up next iteration + }, + delay + ); + + console.log(`Next tweet scheduled in ${randomMinutes} minutes`); }; - // setTimeout(() => { + + if (postImmediately) { + this.generateNewTweet(); + } generateNewTweetLoop(); - // }, 5 * 60 * 1000); // Wait 5 minutes before starting the loop } - + constructor(runtime: IAgentRuntime) { // Initialize the client and pass an optional callback to be called when the client is ready super({ diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts index 2c4e59406c2..f221d41739d 100644 --- a/packages/core/src/runtime.ts +++ b/packages/core/src/runtime.ts @@ -838,10 +838,10 @@ Text: ${attachment.text} recentInteractionsData: Memory[] ): Promise => { // Format the recent messages - const formattedInteractions = await recentInteractionsData - .map(async (message) => { + const formattedInteractions = await Promise.all( + recentInteractionsData.map(async (message) => { const isSelf = message.userId === this.agentId; - let sender; + let sender: string; if (isSelf) { sender = this.character.name; } else { @@ -853,9 +853,9 @@ Text: ${attachment.text} } return `${sender}: ${message.content.text}`; }) - .join("\n"); + ); - return formattedInteractions; + return formattedInteractions.join("\n"); }; const formattedMessageInteractions = diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 962fe0e4f2f..120c312ceec 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -369,6 +369,12 @@ export type Character = { chat: string[]; post: string[]; }; + twitterProfile?: { + username: string; + screenName: string; + bio: string; + nicknames?: string[]; + }; }; export interface IDatabaseAdapter {