Skip to content

Commit

Permalink
Merge branch 'develop' into issue-16
Browse files Browse the repository at this point in the history
  • Loading branch information
wtfsayo authored Jan 15, 2025
2 parents 31fe19e + 5f1ab1c commit 7f0e2fd
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 76 deletions.
47 changes: 22 additions & 25 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ SUPABASE_ANON_KEY=
# Remote character url (optional)
REMOTE_CHARACTER_URL=


###############################
#### Client Configurations ####
###############################
Expand Down Expand Up @@ -242,12 +241,11 @@ LARGE_VOLENGINE_MODEL= # Default: doubao-pro-256k
VOLENGINE_EMBEDDING_MODEL= # Default: doubao-embedding

# DeepSeek Configuration
DEEPSEEK_API_KEY= #Your DeepSeek API key
DEEPSEEK_API_URL= # Default: https://api.deepseek.com
SMALL_DEEPSEEK_MODEL= # Default: deepseek-chat
MEDIUM_DEEPSEEK_MODEL= # Default: deepseek-chat
LARGE_DEEPSEEK_MODEL= # Default: deepseek-chat

DEEPSEEK_API_KEY= #Your DeepSeek API key
DEEPSEEK_API_URL= # Default: https://api.deepseek.com
SMALL_DEEPSEEK_MODEL= # Default: deepseek-chat
MEDIUM_DEEPSEEK_MODEL= # Default: deepseek-chat
LARGE_DEEPSEEK_MODEL= # Default: deepseek-chat

# fal.ai Configuration
FAL_API_KEY=
Expand Down Expand Up @@ -361,7 +359,7 @@ ZEROG_FLOW_ADDRESS=

# Squid Router
SQUID_SDK_URL=https://apiplus.squidrouter.com # Default: https://apiplus.squidrouter.com
SQUID_INTEGRATOR_ID= # get integrator id through https://docs.squidrouter.com/
SQUID_INTEGRATOR_ID= # get integrator id through https://docs.squidrouter.com/
SQUID_EVM_ADDRESS=
SQUID_EVM_PRIVATE_KEY=
SQUID_API_THROTTLE_INTERVAL= # Default: 0; Used to throttle API calls to avoid rate limiting (in ms)
Expand All @@ -375,22 +373,21 @@ SQUID_API_THROTTLE_INTERVAL= # Default: 0; Used to throttle API calls to avoid r
TEE_MODE=OFF # LOCAL | DOCKER | PRODUCTION
WALLET_SECRET_SALT= # ONLY define if you want to use TEE Plugin, otherwise it will throw errors


# TEE Verifiable Log Configuration
VLOG= # true/false; if you want to use TEE Verifiable Log, set this to "true"
VLOG= # true/false; if you want to use TEE Verifiable Log, set this to "true"

# Galadriel Configuration
GALADRIEL_API_KEY=gal-* # Get from https://dashboard.galadriel.com/
GALADRIEL_API_KEY=gal-* # Get from https://dashboard.galadriel.com/

# Venice Configuration
VENICE_API_KEY= # generate from venice settings
SMALL_VENICE_MODEL= # Default: llama-3.3-70b
MEDIUM_VENICE_MODEL= # Default: llama-3.3-70b
LARGE_VENICE_MODEL= # Default: llama-3.1-405b
IMAGE_VENICE_MODEL= # Default: fluently-xl
VENICE_API_KEY= # generate from venice settings
SMALL_VENICE_MODEL= # Default: llama-3.3-70b
MEDIUM_VENICE_MODEL= # Default: llama-3.3-70b
LARGE_VENICE_MODEL= # Default: llama-3.1-405b
IMAGE_VENICE_MODEL= # Default: fluently-xl

# Akash Chat API Configuration docs: https://chatapi.akash.network/documentation
AKASH_CHAT_API_KEY= # Get from https://chatapi.akash.network/
AKASH_CHAT_API_KEY= # Get from https://chatapi.akash.network/
SMALL_AKASH_CHAT_API_MODEL= # Default: Meta-Llama-3-2-3B-Instruct
MEDIUM_AKASH_CHAT_API_MODEL= # Default: Meta-Llama-3-3-70B-Instruct
LARGE_AKASH_CHAT_API_MODEL= # Default: Meta-Llama-3-1-405B-Instruct-FP8
Expand All @@ -403,12 +400,12 @@ FAL_AI_LORA_PATH=
TAVILY_API_KEY=

# WhatsApp Cloud API Configuration
WHATSAPP_ACCESS_TOKEN= # Permanent access token from Facebook Developer Console
WHATSAPP_PHONE_NUMBER_ID= # Phone number ID from WhatsApp Business API
WHATSAPP_BUSINESS_ACCOUNT_ID= # Business Account ID from Facebook Business Manager
WHATSAPP_WEBHOOK_VERIFY_TOKEN= # Custom string for webhook verification
WHATSAPP_API_VERSION=v17.0 # WhatsApp API version (default: v17.0)
ENABLE_TEE_LOG=false # Set to true to enable TEE logging, only available when running eliza in TEE
WHATSAPP_ACCESS_TOKEN= # Permanent access token from Facebook Developer Console
WHATSAPP_PHONE_NUMBER_ID= # Phone number ID from WhatsApp Business API
WHATSAPP_BUSINESS_ACCOUNT_ID= # Business Account ID from Facebook Business Manager
WHATSAPP_WEBHOOK_VERIFY_TOKEN= # Custom string for webhook verification
WHATSAPP_API_VERSION=v17.0 # WhatsApp API version (default: v17.0)
ENABLE_TEE_LOG=false # Set to true to enable TEE logging, only available when running eliza in TEE

# Flow Blockchain Configuration
FLOW_ADDRESS=
Expand Down Expand Up @@ -505,11 +502,11 @@ INTIFACE_WEBSOCKET_URL=ws://localhost:12345
GIPHY_API_KEY=

# OpenWeather
OPEN_WEATHER_API_KEY= # OpenWeather API key
OPEN_WEATHER_API_KEY= # OpenWeather API key

#GITCOIN Passport
PASSPORT_API_KEY= #Gitcoin Passport key
PASSPORT_SCORER= #Scorer number
PASSPORT_SCORER= #Scorer number

# EchoChambers Configuration
ECHOCHAMBERS_API_URL=http://127.0.0.1:3333
Expand Down
6 changes: 2 additions & 4 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SqliteDatabaseAdapter } from "@elizaos/adapter-sqlite";
import { SupabaseDatabaseAdapter } from "@elizaos/adapter-supabase";
import { AutoClientInterface } from "@elizaos/client-auto";
import { DiscordClientInterface } from "@elizaos/client-discord";
import { FarcasterAgentClient } from "@elizaos/client-farcaster";
import { FarcasterClientInterface } from "@elizaos/client-farcaster";
import { LensAgentClient } from "@elizaos/client-lens";
import { SlackClientInterface } from "@elizaos/client-slack";
import { TelegramClientInterface } from "@elizaos/client-telegram";
Expand Down Expand Up @@ -610,10 +610,8 @@ export async function initializeClients(
}

if (clientTypes.includes(Clients.FARCASTER)) {
// why is this one different :(
const farcasterClient = new FarcasterAgentClient(runtime);
const farcasterClient = await FarcasterClientInterface.start(runtime);
if (farcasterClient) {
farcasterClient.start();
clients.farcaster = farcasterClient;
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/client-farcaster/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { IAgentRuntime, elizaLogger } from "@elizaos/core";
import { NeynarAPIClient, isApiErrorResponse } from "@neynar/nodejs-sdk";
import { NeynarCastResponse, Cast, Profile, FidRequest, CastId } from "./types";
import { FarcasterConfig } from "./environment";

export class FarcasterClient {
runtime: IAgentRuntime;
neynar: NeynarAPIClient;
signerUuid: string;
cache: Map<string, any>;
lastInteractionTimestamp: Date;
farcasterConfig: FarcasterConfig;

constructor(opts: {
runtime: IAgentRuntime;
Expand All @@ -16,12 +18,14 @@ export class FarcasterClient {
neynar: NeynarAPIClient;
signerUuid: string;
cache: Map<string, any>;
farcasterConfig: FarcasterConfig;
}) {
this.cache = opts.cache;
this.runtime = opts.runtime;
this.neynar = opts.neynar;
this.signerUuid = opts.signerUuid;
this.lastInteractionTimestamp = new Date();
this.farcasterConfig = opts.farcasterConfig;
}

async loadCastFromNeynarResponse(neynarResponse: any): Promise<Cast> {
Expand Down
140 changes: 140 additions & 0 deletions packages/client-farcaster/src/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import {
parseBooleanFromText,
IAgentRuntime,
ActionTimelineType,
} from "@elizaos/core";
import { z, ZodError } from "zod";

export const DEFAULT_MAX_CAST_LENGTH = 320;
const DEFAULT_POLL_INTERVAL= 120; // 2 minutes
const DEFAULT_POST_INTERVAL_MIN = 90; // 1.5 hours
const DEFAULT_POST_INTERVAL_MAX = 180; // 3 hours
/**
* This schema defines all required/optional environment settings for Farcaster client
*/
export const farcasterEnvSchema = z.object({
FARCASTER_DRY_RUN: z.boolean(),
FARCASTER_FID: z.number().int().min(1, "Farcaster fid is required"),
MAX_CAST_LENGTH: z.number().int().default(DEFAULT_MAX_CAST_LENGTH),
FARCASTER_POLL_INTERVAL: z.number().int().default(DEFAULT_POLL_INTERVAL),
ENABLE_POST: z.boolean(),
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(),
MAX_ACTIONS_PROCESSING: z.number().int(),
ACTION_TIMELINE_TYPE: z
.nativeEnum(ActionTimelineType)
.default(ActionTimelineType.ForYou),
});

export type FarcasterConfig = z.infer<typeof farcasterEnvSchema>;

function safeParseInt(
value: string | undefined | null,
defaultValue: number
): number {
if (!value) return defaultValue;
const parsed = parseInt(value, 10);
return Number.isNaN(parsed) ? defaultValue : Math.max(1, parsed);
}

/**
* Validates or constructs a FarcasterConfig object using zod,
* taking values from the IAgentRuntime or process.env as needed.
*/
export async function validateFarcasterConfig(
runtime: IAgentRuntime
): Promise<FarcasterConfig> {
try {
const farcasterConfig = {
FARCASTER_DRY_RUN:
parseBooleanFromText(
runtime.getSetting("FARCASTER_DRY_RUN") ||
process.env.FARCASTER_DRY_RUN ||
"false"
),

FARCASTER_FID: safeParseInt(
runtime.getSetting("FARCASTER_FID") ||
process.env.FARCASTER_FID,
0
),

MAX_CAST_LENGTH: safeParseInt(
runtime.getSetting("MAX_CAST_LENGTH") ||
process.env.MAX_CAST_LENGTH,
DEFAULT_MAX_CAST_LENGTH
),

FARCASTER_POLL_INTERVAL: safeParseInt(
runtime.getSetting("FARCASTER_POLL_INTERVAL") ||
process.env.FARCASTER_POLL_INTERVAL,
DEFAULT_POLL_INTERVAL
),

ENABLE_POST: parseBooleanFromText(
runtime.getSetting("ENABLE_POST") ||
process.env.ENABLE_POST ||
"true"
),

POST_INTERVAL_MIN: safeParseInt(
runtime.getSetting("POST_INTERVAL_MIN") ||
process.env.POST_INTERVAL_MIN,
DEFAULT_POST_INTERVAL_MIN
),

POST_INTERVAL_MAX: safeParseInt(
runtime.getSetting("POST_INTERVAL_MAX") ||
process.env.POST_INTERVAL_MAX,
DEFAULT_POST_INTERVAL_MAX
),

ENABLE_ACTION_PROCESSING:
parseBooleanFromText(
runtime.getSetting("ENABLE_ACTION_PROCESSING") ||
process.env.ENABLE_ACTION_PROCESSING ||
"false"
) ?? false,

ACTION_INTERVAL: safeParseInt(
runtime.getSetting("ACTION_INTERVAL") ||
process.env.ACTION_INTERVAL,
5 // 5 minutes
),

POST_IMMEDIATELY:
parseBooleanFromText(
runtime.getSetting("POST_IMMEDIATELY") ||
process.env.POST_IMMEDIATELY ||
"false"
) ?? false,

MAX_ACTIONS_PROCESSING: safeParseInt(
runtime.getSetting("MAX_ACTIONS_PROCESSING") ||
process.env.MAX_ACTIONS_PROCESSING,
1
),

ACTION_TIMELINE_TYPE: (
runtime.getSetting("ACTION_TIMELINE_TYPE") ||
process.env.ACTION_TIMELINE_TYPE ||
ActionTimelineType.ForYou
) as ActionTimelineType,
};

return farcasterEnvSchema.parse(farcasterConfig);
} catch (error) {
if (error instanceof ZodError) {
const errorMessages = error.errors
.map((err) => `${err.path.join(".")}: ${err.message}`)
.join("\n");
throw new Error(
`Farcaster configuration validation failed:\n${errorMessages}`
);
}
throw error;
}
}
74 changes: 51 additions & 23 deletions packages/client-farcaster/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ import { FarcasterClient } from "./client";
import { FarcasterPostManager } from "./post";
import { FarcasterInteractionManager } from "./interactions";
import { Configuration, NeynarAPIClient } from "@neynar/nodejs-sdk";
import { validateFarcasterConfig, FarcasterConfig } from "./environment";

export class FarcasterAgentClient implements Client {
/**
* A manager that orchestrates all Farcaster operations:
* - client: base operations (Neynar client, hub connection, etc.)
* - posts: autonomous posting logic
* - interactions: handling mentions, replies, likes, etc.
*/
class FarcasterManager {
client: FarcasterClient;
posts: FarcasterPostManager;
interactions: FarcasterInteractionManager;

private signerUuid: string;

constructor(
public runtime: IAgentRuntime,
client?: FarcasterClient
) {
constructor(runtime: IAgentRuntime, farcasterConfig: FarcasterConfig) {
const cache = new Map<string, any>();

this.signerUuid = runtime.getSetting("FARCASTER_NEYNAR_SIGNER_UUID")!;

const neynarConfig = new Configuration({
Expand All @@ -25,31 +27,28 @@ export class FarcasterAgentClient implements Client {

const neynarClient = new NeynarAPIClient(neynarConfig);

this.client =
client ??
new FarcasterClient({
runtime,
ssl: true,
url:
runtime.getSetting("FARCASTER_HUB_URL") ??
"hub.pinata.cloud",
neynar: neynarClient,
signerUuid: this.signerUuid,
cache,
});

elizaLogger.info("Farcaster Neynar client initialized.");
this.client = new FarcasterClient({
runtime,
ssl: true,
url: runtime.getSetting("FARCASTER_HUB_URL") ?? "hub.pinata.cloud",
neynar: neynarClient,
signerUuid: this.signerUuid,
cache,
farcasterConfig,
});

elizaLogger.success("Farcaster Neynar client initialized.");

this.posts = new FarcasterPostManager(
this.client,
this.runtime,
runtime,
this.signerUuid,
cache
);

this.interactions = new FarcasterInteractionManager(
this.client,
this.runtime,
runtime,
this.signerUuid,
cache
);
Expand All @@ -63,3 +62,32 @@ export class FarcasterAgentClient implements Client {
await Promise.all([this.posts.stop(), this.interactions.stop()]);
}
}

export const FarcasterClientInterface: Client = {
async start(runtime: IAgentRuntime) {
const farcasterConfig = await validateFarcasterConfig(runtime);

elizaLogger.log("Farcaster client started");

const manager = new FarcasterManager(runtime, farcasterConfig);

// Start all services
await manager.start();
runtime.clients.farcaster = manager;
return manager;
},

async stop(runtime: IAgentRuntime) {
try {
// stop it
elizaLogger.log("Stopping farcaster client", runtime.agentId);
if (runtime.clients.farcaster) {
await runtime.clients.farcaster.stop();
}
} catch (e) {
elizaLogger.error("client-farcaster interface stop error", e);
}
},
};

export default FarcasterClientInterface;
Loading

0 comments on commit 7f0e2fd

Please sign in to comment.