From 407cc91bf2ab3d5df85b0b992cf8712e033a02c1 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 00:50:30 +0000 Subject: [PATCH 01/18] isFalsish() for parsing environment in a standard way --- packages/core/src/parsing.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/core/src/parsing.ts b/packages/core/src/parsing.ts index 107ce8ea0bd..bec9e1a0ec0 100644 --- a/packages/core/src/parsing.ts +++ b/packages/core/src/parsing.ts @@ -205,3 +205,28 @@ export const parseActionResponseFromText = ( return { actions }; }; + +export const isFalsish = (input: any): boolean => { + // If the input is exactly NaN, return true + if (Number.isNaN(input)) { + return true; + } + + // Convert input to a string if it's not null or undefined + const value = input == null ? "" : String(input); + + // List of common falsish string representations + const falsishValues = [ + "false", + "0", + "no", + "n", + "off", + "null", + "undefined", + "", + ]; + + // Check if the value (trimmed and lowercased) is in the falsish list + return falsishValues.includes(value.trim().toLowerCase()); +} \ No newline at end of file From af3e8075b9166c47140457d609e2367797995333 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 01:25:14 +0000 Subject: [PATCH 02/18] roll isFalsish into parseBooleanFromText --- packages/core/src/parsing.ts | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/packages/core/src/parsing.ts b/packages/core/src/parsing.ts index bec9e1a0ec0..65e358cdd6c 100644 --- a/packages/core/src/parsing.ts +++ b/packages/core/src/parsing.ts @@ -44,10 +44,11 @@ export const booleanFooter = `Respond with only a YES or a NO.`; * @returns {boolean|null} - Returns `true` for affirmative inputs, `false` for negative inputs, and `null` for unrecognized inputs or null/undefined. */ export const parseBooleanFromText = (text: string) => { + // "NULL", "UNDEFINED" if (!text) return null; // Handle null or undefined input - const affirmative = ["YES", "Y", "TRUE", "T", "1", "ON", "ENABLE"]; - const negative = ["NO", "N", "FALSE", "F", "0", "OFF", "DISABLE"]; + const affirmative = ["YES", "Y", "TRUE", "T", "1", "ON", "ENABLE"]; + const negative = ["NO", "N", "FALSE", "F", "0", "OFF", "DISABLE", ""]; const normalizedText = text.trim().toUpperCase(); @@ -205,28 +206,3 @@ export const parseActionResponseFromText = ( return { actions }; }; - -export const isFalsish = (input: any): boolean => { - // If the input is exactly NaN, return true - if (Number.isNaN(input)) { - return true; - } - - // Convert input to a string if it's not null or undefined - const value = input == null ? "" : String(input); - - // List of common falsish string representations - const falsishValues = [ - "false", - "0", - "no", - "n", - "off", - "null", - "undefined", - "", - ]; - - // Check if the value (trimmed and lowercased) is in the falsish list - return falsishValues.includes(value.trim().toLowerCase()); -} \ No newline at end of file From a962ee7a9c8af5262d940a4023493f3d3eddbc99 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:05:33 +0000 Subject: [PATCH 03/18] lint: remove unused vars --- packages/plugin-starknet/src/actions/unruggable.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/plugin-starknet/src/actions/unruggable.ts b/packages/plugin-starknet/src/actions/unruggable.ts index 7efa66aa1fc..55c961e6287 100644 --- a/packages/plugin-starknet/src/actions/unruggable.ts +++ b/packages/plugin-starknet/src/actions/unruggable.ts @@ -9,29 +9,17 @@ import { Memory, ModelClass, State, - type Action, } from "@elizaos/core"; import { Percent } from "@uniswap/sdk-core"; import { createMemecoin, launchOnEkubo } from "unruggable-sdk"; -import { constants } from "starknet"; import { getStarknetAccount, getStarknetProvider, - parseFormatedAmount, - parseFormatedPercentage, } from "../utils/index.ts"; -import { DeployData, Factory } from "@unruggable_starknet/core"; -import { AMM, QUOTE_TOKEN_SYMBOL } from "@unruggable_starknet/core/constants"; import { ACCOUNTS, TOKENS } from "../utils/constants.ts"; import { validateStarknetConfig } from "../environment.ts"; -interface SwapContent { - sellTokenAddress: string; - buyTokenAddress: string; - sellAmount: string; -} - interface DeployTokenContent { name: string; symbol: string; From c4e120b46334e91be1d7a4387642eb515ae016f7 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:05:55 +0000 Subject: [PATCH 04/18] lint: remove unused error variable --- packages/client-github/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client-github/src/index.ts b/packages/client-github/src/index.ts index e43d194f22e..bf3d12f84df 100644 --- a/packages/client-github/src/index.ts +++ b/packages/client-github/src/index.ts @@ -80,7 +80,7 @@ export class GitHubClient { await this.git.clone(repositoryUrl, this.repoPath); elizaLogger.log(`Successfully cloned repository from ${repositoryUrl}`); return; - } catch (error) { + } catch { elizaLogger.error(`Failed to clone repository from ${repositoryUrl}. Retrying...`); retries++; if (retries === maxRetries) { From bbbf3255311c7ac582555ff514b9eb9bbdd43ad0 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:06:48 +0000 Subject: [PATCH 05/18] lint: fix EmbeddingProvider already defined error (via claude) --- packages/core/src/embedding.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/core/src/embedding.ts b/packages/core/src/embedding.ts index 767b6b5673b..efdc0b49987 100644 --- a/packages/core/src/embedding.ts +++ b/packages/core/src/embedding.ts @@ -14,22 +14,22 @@ interface EmbeddingOptions { provider?: string; } -export const EmbeddingProvider = { +// Define the providers as a const object +export const EMBEDDING_PROVIDERS = { OpenAI: "OpenAI", Ollama: "Ollama", GaiaNet: "GaiaNet", BGE: "BGE", } as const; -export type EmbeddingProvider = - (typeof EmbeddingProvider)[keyof typeof EmbeddingProvider]; +// Create type from the values +export type EmbeddingProvider = typeof EMBEDDING_PROVIDERS[keyof typeof EMBEDDING_PROVIDERS]; -export namespace EmbeddingProvider { - export type OpenAI = typeof EmbeddingProvider.OpenAI; - export type Ollama = typeof EmbeddingProvider.Ollama; - export type GaiaNet = typeof EmbeddingProvider.GaiaNet; - export type BGE = typeof EmbeddingProvider.BGE; -} +// If you need individual types, use type aliases instead of namespace +export type OpenAIProvider = typeof EMBEDDING_PROVIDERS.OpenAI; +export type OllamaProvider = typeof EMBEDDING_PROVIDERS.Ollama; +export type GaiaNetProvider = typeof EMBEDDING_PROVIDERS.GaiaNet; +export type BGEProvider = typeof EMBEDDING_PROVIDERS.BGE; export type EmbeddingConfig = { readonly dimensions: number; From 5abaec79a2380b10e5903bf4fe34efa41716297b Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:07:25 +0000 Subject: [PATCH 06/18] empty is already mapped to null --- packages/core/src/parsing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/parsing.ts b/packages/core/src/parsing.ts index 65e358cdd6c..fba63712f20 100644 --- a/packages/core/src/parsing.ts +++ b/packages/core/src/parsing.ts @@ -48,7 +48,7 @@ export const parseBooleanFromText = (text: string) => { if (!text) return null; // Handle null or undefined input const affirmative = ["YES", "Y", "TRUE", "T", "1", "ON", "ENABLE"]; - const negative = ["NO", "N", "FALSE", "F", "0", "OFF", "DISABLE", ""]; + const negative = ["NO", "N", "FALSE", "F", "0", "OFF", "DISABLE"]; const normalizedText = text.trim().toUpperCase(); From 0abe60824411e412411b06fab38220f0c3fcf0e7 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:08:14 +0000 Subject: [PATCH 07/18] move all env parsing here --- packages/client-twitter/src/environment.ts | 139 +++++++++++++++++++-- 1 file changed, 128 insertions(+), 11 deletions(-) diff --git a/packages/client-twitter/src/environment.ts b/packages/client-twitter/src/environment.ts index a5a5bbf82e8..80e47338fcd 100644 --- a/packages/client-twitter/src/environment.ts +++ b/packages/client-twitter/src/environment.ts @@ -1,34 +1,103 @@ -import { IAgentRuntime } from "@elizaos/core"; +import { parseBooleanFromText, IAgentRuntime } from "@elizaos/core"; import { z } from "zod"; - export const DEFAULT_MAX_TWEET_LENGTH = 280; +const twitterUsernameSchema = z.string() + .min(1) + .max(15) + .regex(/^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]$|^[A-Za-z]$/, 'Invalid Twitter username format'); + export const twitterEnvSchema = z.object({ - TWITTER_DRY_RUN: z - .string() - .transform((val) => val.toLowerCase() === "true"), + TWITTER_DRY_RUN: z.boolean(), TWITTER_USERNAME: z.string().min(1, "Twitter username is required"), TWITTER_PASSWORD: z.string().min(1, "Twitter password is required"), TWITTER_EMAIL: z.string().email("Valid Twitter email is required"), MAX_TWEET_LENGTH: z .string() - .pipe(z.coerce.number().min(0).int()) + .pipe(z.coerce.number().min(1).int()) .default(DEFAULT_MAX_TWEET_LENGTH.toString()), + TWITTER_SEARCH_ENABLE: z.boolean().default(false), + TWITTER_2FA_SECRET: z.string(), + TWITTER_RETRY_LIMIT: z.number().int(), + TWITTER_POLL_INTERVAL: z.number().int(), + TWITTER_TARGET_USERS: z.array(twitterUsernameSchema).default([]), + // I guess it's possible to do the transformation with zod + // not sure it's preferable, maybe a readability issue + // since more people will know js/ts than zod + /* + z + .string() + .transform((val) => val.trim()) + .pipe( + z.string() + .transform((val) => + val ? val.split(',').map((u) => u.trim()).filter(Boolean) : [] + ) + .pipe( + z.array( + z.string() + .min(1) + .max(15) + .regex( + /^[A-Za-z][A-Za-z0-9_]*[A-Za-z0-9]$|^[A-Za-z]$/, + 'Invalid Twitter username format' + ) + ) + ) + .transform((users) => users.join(',')) + ) + .optional() + .default(''), + */ + POST_INTERVAL_MIN: z.number().int(), + POST_INTERVAL_MAX: z.number().int(), + ENABLE_ACTION_PROCESSING: z.boolean(), + ACTION_INTERVAL: z.number().int(), + POST_IMMEDIATELY: z.boolean(), }); export type TwitterConfig = z.infer; +function parseTargetUsers(targetUsersStr?:string | null): string[] { + if (!targetUsersStr?.trim()) { + return []; + } + + return targetUsersStr + .split(',') + .map(user => user.trim()) + .filter(Boolean); // Remove empty usernames + /* + .filter(user => { + // Twitter username validation (basic example) + return user && /^[A-Za-z0-9_]{1,15}$/.test(user); + }); + */ +} + +function safeParseInt(value: string | undefined | null, defaultValue: number): number { + if (!value) return defaultValue; + const parsed = parseInt(value, 10); + return isNaN(parsed) ? defaultValue : Math.max(1, parsed); +} + +// This also is organized to serve as a point of documentation for the client +// most of the inputs from the framework (env/character) + +// we also do a lot of typing/parsing here +// so we can do it once and only once per character export async function validateTwitterConfig( runtime: IAgentRuntime ): Promise { try { const twitterConfig = { TWITTER_DRY_RUN: - runtime.getSetting("TWITTER_DRY_RUN") || - process.env.TWITTER_DRY_RUN || - "false", + parseBooleanFromText( + runtime.getSetting("TWITTER_DRY_RUN") || + process.env.TWITTER_DRY_RUN + ) ?? false, // parseBooleanFromText return null if "", map "" to false TWITTER_USERNAME: - runtime.getSetting("TWITTER_USERNAME") || + runtime.getSetting ("TWITTER_USERNAME") || process.env.TWITTER_USERNAME, TWITTER_PASSWORD: runtime.getSetting("TWITTER_PASSWORD") || @@ -36,10 +105,58 @@ export async function validateTwitterConfig( TWITTER_EMAIL: runtime.getSetting("TWITTER_EMAIL") || process.env.TWITTER_EMAIL, - MAX_TWEET_LENGTH: + MAX_TWEET_LENGTH: // number as string? runtime.getSetting("MAX_TWEET_LENGTH") || process.env.MAX_TWEET_LENGTH || DEFAULT_MAX_TWEET_LENGTH.toString(), + TWITTER_SEARCH_ENABLE: // bool + parseBooleanFromText( + runtime.getSetting("TWITTER_SEARCH_ENABLE") || + process.env.TWITTER_SEARCH_ENABLE + ) ?? false, + TWITTER_2FA_SECRET: // string passthru + runtime.getSetting("TWITTER_2FA_SECRET") || + process.env.TWITTER_2FA_SECRET || "", + TWITTER_RETRY_LIMIT: // int + safeParseInt( + runtime.getSetting("TWITTER_RETRY_LIMIT") || + process.env.TWITTER_RETRY_LIMIT + , 5), + TWITTER_POLL_INTERVAL: // int in minutes + safeParseInt( + runtime.getSetting("TWITTER_POLL_INTERVAL") || + process.env.TWITTER_POLL_INTERVAL + , 120), // 2h + TWITTER_TARGET_USERS: // comma separated string + parseTargetUsers( + runtime.getSetting("TWITTER_TARGET_USERS") || + process.env.TWITTER_TARGET_USERS + ), + POST_INTERVAL_MIN: // int in minutes + safeParseInt( + runtime.getSetting("POST_INTERVAL_MIN") || + process.env.POST_INTERVAL_MIN + , 90), // 1.5 hours + POST_INTERVAL_MAX: // int in minutes + safeParseInt( + runtime.getSetting("POST_INTERVAL_MAX") || + process.env.POST_INTERVAL_MAX + , 180), // 3 hours + ENABLE_ACTION_PROCESSING: // bool + parseBooleanFromText( + runtime.getSetting("ENABLE_ACTION_PROCESSING") || + process.env.ENABLE_ACTION_PROCESSING + ) ?? false, + ACTION_INTERVAL: // int in minutes (min 1m) + safeParseInt( + runtime.getSetting("ACTION_INTERVAL") || + process.env.ACTION_INTERVAL + , 5), // 5 minutes + POST_IMMEDIATELY: // bool + parseBooleanFromText( + runtime.getSetting("POST_IMMEDIATELY") || + process.env.POST_IMMEDIATELY + ) ?? false, }; return twitterEnvSchema.parse(twitterConfig); From 08901de3abffba4b6291beaf4db6d5dc514f8335 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:08:46 +0000 Subject: [PATCH 08/18] add twitterConfig cstr param and prefer it over getSetting --- packages/client-twitter/src/base.ts | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/client-twitter/src/base.ts b/packages/client-twitter/src/base.ts index 1b04485d6d2..ed4f8481496 100644 --- a/packages/client-twitter/src/base.ts +++ b/packages/client-twitter/src/base.ts @@ -16,6 +16,7 @@ import { Tweet, } from "agent-twitter-client"; import { EventEmitter } from "events"; +import { TwitterConfig } from "./environment.ts"; export function extractAnswer(text: string): string { const startIndex = text.indexOf("Answer: ") + 8; @@ -85,6 +86,7 @@ export class ClientBase extends EventEmitter { static _twitterClients: { [accountIdentifier: string]: Scraper } = {}; twitterClient: Scraper; runtime: IAgentRuntime; + twitterConfig: TwitterConfig; directions: string; lastCheckedTweetId: bigint | null = null; imageDescriptionService: IImageDescriptionService; @@ -134,10 +136,11 @@ export class ClientBase extends EventEmitter { ); } - constructor(runtime: IAgentRuntime) { + constructor(runtime: IAgentRuntime, twitterConfig:TwitterConfig) { super(); this.runtime = runtime; - const username = this.runtime.getSetting("TWITTER_USERNAME"); + this.twitterConfig = twitterConfig; + const username = twitterConfig.TWITTER_USERNAME; if (ClientBase._twitterClients[username]) { this.twitterClient = ClientBase._twitterClients[username]; } else { @@ -153,15 +156,11 @@ export class ClientBase extends EventEmitter { } async init() { - const username = this.runtime.getSetting("TWITTER_USERNAME"); - const password = this.runtime.getSetting("TWITTER_PASSWORD"); - const email = this.runtime.getSetting("TWITTER_EMAIL"); - let retries = parseInt( - this.runtime.getSetting("TWITTER_RETRY_LIMIT") || "5", - 10 - ); - const twitter2faSecret = - this.runtime.getSetting("TWITTER_2FA_SECRET") || undefined; + const username = this.twitterConfig.TWITTER_USERNAME; + const password = this.twitterConfig.TWITTER_PASSWORD; + const email = this.twitterConfig.TWITTER_EMAIL; + let retries = this.twitterConfig.TWITTER_RETRY_LIMIT + const twitter2faSecret = this.twitterConfig.TWITTER_2FA_SECRET; if (!username) { throw new Error("Twitter username not configured"); @@ -314,7 +313,7 @@ export class ClientBase extends EventEmitter { async fetchTimelineForActions(count: number): Promise { elizaLogger.debug("fetching timeline for actions"); - const agentUsername = this.runtime.getSetting("TWITTER_USERNAME"); + const agentUsername = this.twitterConfig.TWITTER_USERNAME const homeTimeline = await this.twitterClient.fetchHomeTimeline( count, [] @@ -510,7 +509,7 @@ export class ClientBase extends EventEmitter { } const timeline = await this.fetchHomeTimeline(cachedTimeline ? 10 : 50); - const username = this.runtime.getSetting("TWITTER_USERNAME"); + const username = this.twitterConfig.TWITTER_USERNAME; // Get the most recent 20 mentions and interactions const mentionsAndInteractions = await this.fetchSearchTweets( From 5403d9e961ffc6fc9b334e1053f45a103b1d760e Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:09:08 +0000 Subject: [PATCH 09/18] prefer this.client.twitterConfig over getSetting --- packages/client-twitter/src/interactions.ts | 32 ++++++--------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/packages/client-twitter/src/interactions.ts b/packages/client-twitter/src/interactions.ts index 438445ecbeb..3274cd32308 100644 --- a/packages/client-twitter/src/interactions.ts +++ b/packages/client-twitter/src/interactions.ts @@ -100,9 +100,8 @@ export class TwitterInteractionClient { this.handleTwitterInteractions(); setTimeout( handleTwitterInteractionsLoop, - Number( - this.runtime.getSetting("TWITTER_POLL_INTERVAL") || 120 - ) * 1000 // Default to 2 minutes + // Defaults to 2 minutes + this.client.twitterConfig.TWITTER_POLL_INTERVAL * 1000 ); }; handleTwitterInteractionsLoop(); @@ -110,8 +109,6 @@ export class TwitterInteractionClient { async handleTwitterInteractions() { elizaLogger.log("Checking Twitter interactions"); - // Read from environment variable, fallback to default list if not set - const targetUsersStr = this.runtime.getSetting("TWITTER_TARGET_USERS"); const twitterUsername = this.client.profile.username; try { @@ -130,11 +127,8 @@ export class TwitterInteractionClient { ); let uniqueTweetCandidates = [...mentionCandidates]; // Only process target users if configured - if (targetUsersStr && targetUsersStr.trim()) { - const TARGET_USERS = targetUsersStr - .split(",") - .map((u) => u.trim()) - .filter((u) => u.length > 0); // Filter out empty strings after split + if (this.client.twitterConfig.TWITTER_TARGET_USERS.length) { + const TARGET_USERS = this.client.twitterConfig.TWITTER_TARGET_USERS; elizaLogger.log("Processing target users:", TARGET_USERS); @@ -347,7 +341,7 @@ export class TwitterInteractionClient { let state = await this.runtime.composeState(message, { twitterClient: this.client.twitterClient, - twitterUserName: this.runtime.getSetting("TWITTER_USERNAME"), + twitterUserName: this.client.twitterConfig.TWITTER_USERNAME, currentPost, formattedConversation, }); @@ -383,18 +377,8 @@ export class TwitterInteractionClient { this.client.saveRequestMessage(message, state); } - // 1. Get the raw target users string from settings - const targetUsersStr = this.runtime.getSetting("TWITTER_TARGET_USERS"); - - // 2. Process the string to get valid usernames - const validTargetUsersStr = - targetUsersStr && targetUsersStr.trim() - ? targetUsersStr - .split(",") // Split by commas: "user1,user2" -> ["user1", "user2"] - .map((u) => u.trim()) // Remove whitespace: [" user1 ", "user2 "] -> ["user1", "user2"] - .filter((u) => u.length > 0) - .join(",") - : ""; + // get usernames into str + const validTargetUsersStr = this.client.twitterConfig.TWITTER_TARGET_USERS.join(","); const shouldRespondContext = composeContext({ state, @@ -450,7 +434,7 @@ export class TwitterInteractionClient { this.client, response, message.roomId, - this.runtime.getSetting("TWITTER_USERNAME"), + this.client.twitterConfig.TWITTER_USERNAME, tweet.id ); return memories; From ad170788d0c537dbe140d5c8261a8f85da366f7d Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:09:44 +0000 Subject: [PATCH 10/18] prefer client.twitterConfig over getSetting --- packages/client-twitter/src/utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/client-twitter/src/utils.ts b/packages/client-twitter/src/utils.ts index 0e7db5d9868..ac93ef93ca1 100644 --- a/packages/client-twitter/src/utils.ts +++ b/packages/client-twitter/src/utils.ts @@ -4,7 +4,6 @@ import { Content, Memory, UUID } from "@elizaos/core"; import { stringToUuid } from "@elizaos/core"; import { ClientBase } from "./base"; import { elizaLogger } from "@elizaos/core"; -import { DEFAULT_MAX_TWEET_LENGTH } from "./environment"; import { Media } from "@elizaos/core"; import fs from "fs"; import path from "path"; @@ -174,8 +173,7 @@ export async function sendTweet( ): Promise { const tweetChunks = splitTweetContent( content.text, - Number(client.runtime.getSetting("MAX_TWEET_LENGTH")) || - DEFAULT_MAX_TWEET_LENGTH + client.twitterConfig.MAX_TWEET_LENGTH ); const sentTweets: Tweet[] = []; let previousTweetId = inReplyTo; From 09d49788ae8dd866fd7af9e8079de8d8772e6e92 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:10:21 +0000 Subject: [PATCH 11/18] prefer this.client.twitterConfig over getSetting --- packages/client-twitter/src/search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client-twitter/src/search.ts b/packages/client-twitter/src/search.ts index 8934abf72e3..37254ed1a7f 100644 --- a/packages/client-twitter/src/search.ts +++ b/packages/client-twitter/src/search.ts @@ -51,7 +51,7 @@ export class TwitterSearchClient { constructor(client: ClientBase, runtime: IAgentRuntime) { this.client = client; this.runtime = runtime; - this.twitterUsername = runtime.getSetting("TWITTER_USERNAME"); + this.twitterUsername = this.client.twitterConfig.TWITTER_USERNAME; } async start() { From ecaf55411957f985c1ee288a8a6e37393196cd57 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:10:50 +0000 Subject: [PATCH 12/18] prefer this.client.twitterConfig over getSettings --- packages/client-twitter/src/post.ts | 60 +++++++++-------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index cd7b8b9d6ec..41466c5ba3d 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -6,7 +6,6 @@ import { IAgentRuntime, ModelClass, stringToUuid, - parseBooleanFromText, UUID, } from "@elizaos/core"; import { elizaLogger } from "@elizaos/core"; @@ -106,10 +105,8 @@ export class TwitterPostClient { constructor(client: ClientBase, runtime: IAgentRuntime) { this.client = client; this.runtime = runtime; - this.twitterUsername = runtime.getSetting("TWITTER_USERNAME"); - this.isDryRun = parseBooleanFromText( - runtime.getSetting("TWITTER_DRY_RUN") ?? "false" - ); + this.twitterUsername = this.client.twitterConfig.TWITTER_USERNAME; + this.isDryRun = this.client.twitterConfig.TWITTER_DRY_RUN // Log configuration on initialization elizaLogger.log("Twitter Client Configuration:"); @@ -118,34 +115,34 @@ export class TwitterPostClient { `- Dry Run Mode: ${this.isDryRun ? "enabled" : "disabled"}` ); elizaLogger.log( - `- Post Interval: ${runtime.getSetting("POST_INTERVAL_MIN") || "90"}-${runtime.getSetting("POST_INTERVAL_MAX") || "180"} minutes` + `- Post Interval: ${this.client.twitterConfig.POST_INTERVAL_MIN}-${this.client.twitterConfig.POST_INTERVAL_MAX} minutes` ); elizaLogger.log( - `- Action Processing: ${parseBooleanFromText(runtime.getSetting("ENABLE_ACTION_PROCESSING") ?? "false") ? "enabled" : "disabled"}` + `- Action Processing: ${this.client.twitterConfig.ENABLE_ACTION_PROCESSING ? "enabled" : "disabled"}` ); elizaLogger.log( - `- Action Interval: ${(parseInt(runtime.getSetting("ACTION_INTERVAL") ?? "300000") / 1000).toFixed(0)} seconds` + `- Action Interval: ${this.client.twitterConfig.ACTION_INTERVAL} seconds` ); elizaLogger.log( - `- Post Immediately: ${parseBooleanFromText(runtime.getSetting("POST_IMMEDIATELY") ?? "false") ? "enabled" : "disabled"}` + `- Post Immediately: ${this.client.twitterConfig.POST_IMMEDIATELY ? "enabled" : "disabled"}` ); elizaLogger.log( - `- Search Enabled: ${parseBooleanFromText(runtime.getSetting("TWITTER_SEARCH_ENABLE") ?? "false") ? "enabled" : "disabled"}` + `- Search Enabled: ${this.client.twitterConfig.TWITTER_SEARCH_ENABLE ? "enabled" : "disabled"}` ); - const targetUsers = runtime.getSetting("TWITTER_TARGET_USERS"); + const targetUsers = this.client.twitterConfig.TWITTER_TARGET_USERS; if (targetUsers) { elizaLogger.log(`- Target Users: ${targetUsers}`); } if (this.isDryRun) { elizaLogger.log( - "Twitter client initialized in dry run mode - no actual tweets will be posted" + "Twitter client initialized in dry run mode - no actual tweets should be posted" ); } } - async start(postImmediately: boolean = false) { + async start() { if (!this.client.profile) { await this.client.init(); } @@ -156,10 +153,8 @@ export class TwitterPostClient { }>("twitter/" + this.twitterUsername + "/lastPost"); const lastPostTimestamp = lastPost?.timestamp ?? 0; - const minMinutes = - parseInt(this.runtime.getSetting("POST_INTERVAL_MIN")) || 90; - const maxMinutes = - parseInt(this.runtime.getSetting("POST_INTERVAL_MAX")) || 180; + const minMinutes = this.client.twitterConfig.POST_INTERVAL_MIN; + const maxMinutes = this.client.twitterConfig.POST_INTERVAL_MAX; const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes; @@ -177,8 +172,7 @@ export class TwitterPostClient { }; const processActionsLoop = async () => { - const actionInterval = - parseInt(this.runtime.getSetting("ACTION_INTERVAL")) || 300000; // Default to 5 minutes + const actionInterval = this.client.twitterConfig.ACTION_INTERVAL; // Defaults to 5 minutes while (!this.stopProcessingActions) { try { @@ -190,7 +184,7 @@ export class TwitterPostClient { ); // Wait for the full interval before next processing await new Promise((resolve) => - setTimeout(resolve, actionInterval) + setTimeout(resolve, actionInterval * 60 * 1000) // now in minutes ); } } catch (error) { @@ -204,16 +198,7 @@ export class TwitterPostClient { } }; - if ( - this.runtime.getSetting("POST_IMMEDIATELY") != null && - this.runtime.getSetting("POST_IMMEDIATELY") !== "" - ) { - // Retrieve setting, default to false if not set or if the value is not "true" - postImmediately = - this.runtime.getSetting("POST_IMMEDIATELY") === "true" || false; - } - - if (postImmediately) { + if (this.client.twitterConfig.POST_IMMEDIATELY) { await this.generateNewTweet(); } @@ -225,12 +210,7 @@ export class TwitterPostClient { elizaLogger.log("Tweet generation loop disabled (dry run mode)"); } - // Add check for ENABLE_ACTION_PROCESSING before starting the loop - const enableActionProcessing = parseBooleanFromText( - this.runtime.getSetting("ENABLE_ACTION_PROCESSING") ?? "false" - ); - - if (enableActionProcessing && !this.isDryRun) { + if (this.client.twitterConfig.ENABLE_ACTION_PROCESSING && !this.isDryRun) { processActionsLoop().catch((error) => { elizaLogger.error( "Fatal error in process actions loop:", @@ -333,8 +313,7 @@ export class TwitterPostClient { // Note Tweet failed due to authorization. Falling back to standard Tweet. const truncateContent = truncateToCompleteSentence( content, - parseInt(runtime.getSetting("MAX_TWEET_LENGTH")) || - DEFAULT_MAX_TWEET_LENGTH + this.client.twitterConfig.MAX_TWEET_LENGTH ); return await this.sendStandardTweet( client, @@ -496,10 +475,7 @@ export class TwitterPostClient { } // Truncate the content to the maximum tweet length specified in the environment settings, ensuring the truncation respects sentence boundaries. - const maxTweetLength = parseInt( - this.runtime.getSetting("MAX_TWEET_LENGTH"), - 10 - ); + const maxTweetLength = this.client.twitterConfig.MAX_TWEET_LENGTH if (maxTweetLength) { cleanedContent = truncateToCompleteSentence( cleanedContent, From d7d655ff9e61b39b6c46e2166fc5e241ac5cdf63 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:11:37 +0000 Subject: [PATCH 13/18] pass twitterConfig as 2nd parameter to cstr, only start search once --- packages/client-twitter/src/index.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/client-twitter/src/index.ts b/packages/client-twitter/src/index.ts index 3692525a240..0da22e7d6e3 100644 --- a/packages/client-twitter/src/index.ts +++ b/packages/client-twitter/src/index.ts @@ -1,6 +1,6 @@ import { Client, elizaLogger, IAgentRuntime } from "@elizaos/core"; import { ClientBase } from "./base.ts"; -import { validateTwitterConfig } from "./environment.ts"; +import { validateTwitterConfig, TwitterConfig } from "./environment.ts"; import { TwitterInteractionClient } from "./interactions.ts"; import { TwitterPostClient } from "./post.ts"; import { TwitterSearchClient } from "./search.ts"; @@ -10,11 +10,11 @@ class TwitterManager { post: TwitterPostClient; search: TwitterSearchClient; interaction: TwitterInteractionClient; - constructor(runtime: IAgentRuntime, enableSearch: boolean) { - this.client = new ClientBase(runtime); + constructor(runtime: IAgentRuntime, twitterConfig:TwitterConfig) { + this.client = new ClientBase(runtime, twitterConfig); this.post = new TwitterPostClient(this.client, runtime); - if (enableSearch) { + if (twitterConfig.TWITTER_SEARCH_ENABLE) { // this searches topics from character file elizaLogger.warn("Twitter/X client running in a mode that:"); elizaLogger.warn("1. violates consent of random users"); @@ -30,11 +30,11 @@ class TwitterManager { export const TwitterClientInterface: Client = { async start(runtime: IAgentRuntime) { - await validateTwitterConfig(runtime); + const twitterConfig:TwitterConfig = await validateTwitterConfig(runtime); elizaLogger.log("Twitter client started"); - const manager = new TwitterManager(runtime, runtime.getSetting("TWITTER_SEARCH_ENABLE").toLowerCase() === "true"); + const manager = new TwitterManager(runtime, twitterConfig); await manager.client.init(); @@ -45,8 +45,6 @@ export const TwitterClientInterface: Client = { await manager.interaction.start(); - await manager.search?.start(); - return manager; }, async stop(_runtime: IAgentRuntime) { From 899119068da9317e57c678f47eb0499d3f381c62 Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:32:23 +0000 Subject: [PATCH 14/18] fix TWITTER_POLL_INTERVAL scale --- packages/client-twitter/src/environment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client-twitter/src/environment.ts b/packages/client-twitter/src/environment.ts index 80e47338fcd..032714e3b74 100644 --- a/packages/client-twitter/src/environment.ts +++ b/packages/client-twitter/src/environment.ts @@ -122,11 +122,11 @@ export async function validateTwitterConfig( runtime.getSetting("TWITTER_RETRY_LIMIT") || process.env.TWITTER_RETRY_LIMIT , 5), - TWITTER_POLL_INTERVAL: // int in minutes + TWITTER_POLL_INTERVAL: // int in seconds safeParseInt( runtime.getSetting("TWITTER_POLL_INTERVAL") || process.env.TWITTER_POLL_INTERVAL - , 120), // 2h + , 120), // 2m TWITTER_TARGET_USERS: // comma separated string parseTargetUsers( runtime.getSetting("TWITTER_TARGET_USERS") || From 820ac52d4fa8a4e6b1b9acb157c9a2027bb523bc Mon Sep 17 00:00:00 2001 From: odilitime Date: Sat, 28 Dec 2024 02:41:50 +0000 Subject: [PATCH 15/18] make MAX_TWEET_LENGTH integer since that's how it's consomed/compared --- packages/client-twitter/src/environment.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/client-twitter/src/environment.ts b/packages/client-twitter/src/environment.ts index 032714e3b74..8ff2fb454ed 100644 --- a/packages/client-twitter/src/environment.ts +++ b/packages/client-twitter/src/environment.ts @@ -12,10 +12,7 @@ export const twitterEnvSchema = z.object({ TWITTER_USERNAME: z.string().min(1, "Twitter username is required"), TWITTER_PASSWORD: z.string().min(1, "Twitter password is required"), TWITTER_EMAIL: z.string().email("Valid Twitter email is required"), - MAX_TWEET_LENGTH: z - .string() - .pipe(z.coerce.number().min(1).int()) - .default(DEFAULT_MAX_TWEET_LENGTH.toString()), + MAX_TWEET_LENGTH: z.number().int().default(DEFAULT_MAX_TWEET_LENGTH), TWITTER_SEARCH_ENABLE: z.boolean().default(false), TWITTER_2FA_SECRET: z.string(), TWITTER_RETRY_LIMIT: z.number().int(), @@ -106,9 +103,10 @@ export async function validateTwitterConfig( runtime.getSetting("TWITTER_EMAIL") || process.env.TWITTER_EMAIL, MAX_TWEET_LENGTH: // number as string? - runtime.getSetting("MAX_TWEET_LENGTH") || - process.env.MAX_TWEET_LENGTH || - DEFAULT_MAX_TWEET_LENGTH.toString(), + safeParseInt( + runtime.getSetting("MAX_TWEET_LENGTH") || + process.env.MAX_TWEET_LENGTH + , DEFAULT_MAX_TWEET_LENGTH), TWITTER_SEARCH_ENABLE: // bool parseBooleanFromText( runtime.getSetting("TWITTER_SEARCH_ENABLE") || From cf7f98f68f10268c76a4db808078abbf7769fe68 Mon Sep 17 00:00:00 2001 From: Shakker Nerd Date: Sat, 28 Dec 2024 05:03:12 +0000 Subject: [PATCH 16/18] revert: packages/client-github/src/index.ts to match develop --- packages/client-github/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client-github/src/index.ts b/packages/client-github/src/index.ts index 6c848c8d611..92cf3540436 100644 --- a/packages/client-github/src/index.ts +++ b/packages/client-github/src/index.ts @@ -82,9 +82,10 @@ export class GitHubClient { `Successfully cloned repository from ${repositoryUrl}` ); return; - } catch { + } catch (error) { elizaLogger.error( `Failed to clone repository from ${repositoryUrl}. Retrying...`, + error ); retries++; if (retries === maxRetries) { From 821b7923cbe2e626d1162600d257089d98e0e725 Mon Sep 17 00:00:00 2001 From: Shakker Nerd Date: Sat, 28 Dec 2024 07:24:07 +0000 Subject: [PATCH 17/18] chore: revert file to match develop branch --- packages/core/src/embedding.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/core/src/embedding.ts b/packages/core/src/embedding.ts index dab1f5fea43..1c2a72c5b88 100644 --- a/packages/core/src/embedding.ts +++ b/packages/core/src/embedding.ts @@ -14,22 +14,15 @@ interface EmbeddingOptions { provider?: string; } -// Define the providers as a const object -export const EMBEDDING_PROVIDERS = { +export const EmbeddingProvider = { OpenAI: "OpenAI", Ollama: "Ollama", GaiaNet: "GaiaNet", BGE: "BGE", } as const; -// Create type from the values -export type EmbeddingProvider = typeof EMBEDDING_PROVIDERS[keyof typeof EMBEDDING_PROVIDERS]; - -// If you need individual types, use type aliases instead of namespace -export type OpenAIProvider = typeof EMBEDDING_PROVIDERS.OpenAI; -export type OllamaProvider = typeof EMBEDDING_PROVIDERS.Ollama; -export type GaiaNetProvider = typeof EMBEDDING_PROVIDERS.GaiaNet; -export type BGEProvider = typeof EMBEDDING_PROVIDERS.BGE; +export type EmbeddingProviderType = + (typeof EmbeddingProvider)[keyof typeof EmbeddingProvider]; export type EmbeddingConfig = { readonly dimensions: number; From 7d78fcdb1b6b7f15de5837c80a94f9c7e69b6dad Mon Sep 17 00:00:00 2001 From: Shakker Nerd Date: Sat, 28 Dec 2024 07:24:52 +0000 Subject: [PATCH 18/18] chore: revert file to match develop branch --- packages/core/src/parsing.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/core/src/parsing.ts b/packages/core/src/parsing.ts index fba63712f20..107ce8ea0bd 100644 --- a/packages/core/src/parsing.ts +++ b/packages/core/src/parsing.ts @@ -44,11 +44,10 @@ export const booleanFooter = `Respond with only a YES or a NO.`; * @returns {boolean|null} - Returns `true` for affirmative inputs, `false` for negative inputs, and `null` for unrecognized inputs or null/undefined. */ export const parseBooleanFromText = (text: string) => { - // "NULL", "UNDEFINED" if (!text) return null; // Handle null or undefined input - const affirmative = ["YES", "Y", "TRUE", "T", "1", "ON", "ENABLE"]; - const negative = ["NO", "N", "FALSE", "F", "0", "OFF", "DISABLE"]; + const affirmative = ["YES", "Y", "TRUE", "T", "1", "ON", "ENABLE"]; + const negative = ["NO", "N", "FALSE", "F", "0", "OFF", "DISABLE"]; const normalizedText = text.trim().toUpperCase();