Skip to content

Commit

Permalink
Merge pull request #97 from autonomys/ref/agent-frame-config
Browse files Browse the repository at this point in the history
Add zod for config format - clean coding and refactoring
  • Loading branch information
Xm0onh authored Jan 2, 2025
2 parents 18a08c4 + 8a40ba3 commit d542055
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 62 deletions.
24 changes: 12 additions & 12 deletions auto-agents-framework/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,29 @@ MAX_MENTIONS=20
MAX_THREAD_LENGTH=20
MAX_MY_RECENT_TWEETS=10
POST_TWEETS=false
RESPONSE_INTERVAL_MINUTES=26
POST_INTERVAL_MINUTES=30
RESPONSE_INTERVAL_MS=26
POST_INTERVAL_MS=30

# LLM Configuration
LARGE_LLM_MODEL=<large_llm_model>
SMALL_LLM_MODEL=<small_llm_model>
OPENAI_API_KEY=<openai_api_key>

# AutoDrive Configuration
AUTO_DRIVE_API_KEY=<auto_drive_api_key>
AUTO_DRIVE_ENCRYPTION_PASSWORD=<auto_drive_encryption_password>
AUTO_DRIVE_UPLOAD=false

# SC Configuration
# Blockchain Configuration
RPC_URL=<rpc_url>
CONTRACT_ADDRESS=<contract_address>
PRIVATE_KEY=<private_key>

# LLM Configuration
SMALL_LLM_MODEL=gpt-4o-mini
LARGE_LLM_MODEL=<large_llm_model>
OPENAI_API_KEY=<openai_api_key>

# SerpAPI Configuration
SERPAPI_API_KEY=<serpapi_api_key>

# Server Configuration
PORT=<port>

# Environment
NODE_ENV=<node_env>
NODE_ENV=<node_env>

# Retry Limit
RETRY_LIMIT=<retry_limit>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { config } from '../../../config/index.js';
import { wallet } from './agentWallet.js';
import { cidFromBlakeHash, cidToString } from '@autonomys/auto-dag-data';

const CONTRACT_ADDRESS = config.autoDriveConfig.CONTRACT_ADDRESS as `0x${string}`;
const CONTRACT_ADDRESS = config.blockchainConfig.CONTRACT_ADDRESS as `0x${string}`;

const contract = new ethers.Contract(CONTRACT_ADDRESS, MEMORY_ABI, wallet);

Expand Down
4 changes: 2 additions & 2 deletions auto-agents-framework/src/agents/tools/utils/agentWallet.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ethers } from 'ethers';
import { config } from '../../../config/index.js';

const provider = new ethers.JsonRpcProvider(config.autoDriveConfig.RPC_URL);
const provider = new ethers.JsonRpcProvider(config.blockchainConfig.RPC_URL);

export const wallet = new ethers.Wallet(config.autoDriveConfig.PRIVATE_KEY as string, provider);
export const wallet = new ethers.Wallet(config.blockchainConfig.PRIVATE_KEY as string, provider);

export async function signMessage(data: object): Promise<string> {
const message = JSON.stringify(data);
Expand Down
110 changes: 63 additions & 47 deletions auto-agents-framework/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,73 @@
import { z } from 'zod';
import dotenv from 'dotenv';
import { configSchema } from './schema.js';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

// Get the equivalent of __dirname in ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const __dirname = path.dirname(__filename);

dotenv.config();
dotenv.config({ path: path.resolve(__dirname, '../../.env') });

const twitterConfig = {
USERNAME: process.env.TWITTER_USERNAME || '',
PASSWORD: process.env.TWITTER_PASSWORD || '',
COOKIES_PATH: process.env.TWITTER_COOKIES_PATH || 'cookies.json',
NUM_TIMELINE_TWEETS: Number(process.env.NUM_TIMELINE_TWEETS) || 10,
NUM_FOLLOWING_RECENT_TWEETS: Number(process.env.NUM_FOLLOWING_RECENT_TWEETS) || 10,
NUM_RANDOM_FOLLOWERS: Number(process.env.NUM_RANDOM_FOLLOWERS) || 5,
MAX_MENTIONS: Number(process.env.MAX_MENTIONS) || 5,
MAX_THREAD_LENGTH: Number(process.env.MAX_THREAD_LENGTH) || 20,
MAX_MY_RECENT_TWEETS: Number(process.env.MAX_MY_RECENT_TWEETS) || 10,
POST_TWEETS: process.env.POST_TWEETS === 'true',
RESPONSE_INTERVAL_MS: Number(process.env.RESPONSE_INTERVAL_MINUTES) * 60 * 1000 || 26 * 60 * 1000,
POST_INTERVAL_MS: Number(process.env.POST_INTERVAL_MINUTES) * 60 * 1000 || 30 * 60 * 1000,
};
function formatZodError(error: z.ZodError) {
const missingVars = error.issues.map(issue => {
const path = issue.path.join('.');
return `- ${path}: ${issue.message}`;
});
return `Missing or invalid environment variables:
\n${missingVars.join('\n')}
\nPlease check your .env file and ensure all required variables are set correctly.`;
}

const llmConfig = {
LARGE_LLM_MODEL: process.env.LARGE_LLM_MODEL || 'gpt-4o',
SMALL_LLM_MODEL: process.env.SMALL_LLM_MODEL || 'gpt-4o-mini',
OPENAI_API_KEY: process.env.OPENAI_API_KEY || '',
};
export const config = (() => {
try {
const rawConfig = {
twitterConfig: {
USERNAME: process.env.TWITTER_USERNAME || '',
PASSWORD: process.env.TWITTER_PASSWORD || '',
COOKIES_PATH: process.env.TWITTER_COOKIES_PATH || 'cookies.json',
NUM_TIMELINE_TWEETS: Number(process.env.NUM_TIMELINE_TWEETS) || 10,
NUM_FOLLOWING_RECENT_TWEETS: Number(process.env.NUM_FOLLOWING_RECENT_TWEETS) || 10,
NUM_RANDOM_FOLLOWERS: Number(process.env.NUM_RANDOM_FOLLOWERS) || 5,
MAX_MENTIONS: Number(process.env.MAX_MENTIONS) || 5,
MAX_THREAD_LENGTH: Number(process.env.MAX_THREAD_LENGTH) || 20,
MAX_MY_RECENT_TWEETS: Number(process.env.MAX_MY_RECENT_TWEETS) || 10,
POST_TWEETS: process.env.POST_TWEETS === 'true',
RESPONSE_INTERVAL_MS: (Number(process.env.RESPONSE_INTERVAL_MS) || 26) * 60 * 1000,
POST_INTERVAL_MS: (Number(process.env.POST_INTERVAL_MS) || 30) * 60 * 1000,
},
llmConfig: {
LARGE_LLM_MODEL: process.env.LARGE_LLM_MODEL || 'gpt-4',
SMALL_LLM_MODEL: process.env.SMALL_LLM_MODEL || 'gpt-4-mini',
OPENAI_API_KEY: process.env.OPENAI_API_KEY || '',
},
autoDriveConfig: {
AUTO_DRIVE_API_KEY: process.env.AUTO_DRIVE_API_KEY,
AUTO_DRIVE_ENCRYPTION_PASSWORD: process.env.AUTO_DRIVE_ENCRYPTION_PASSWORD,
AUTO_DRIVE_UPLOAD: process.env.AUTO_DRIVE_UPLOAD === 'true',
},
blockchainConfig: {
RPC_URL: process.env.RPC_URL || undefined,
CONTRACT_ADDRESS: process.env.CONTRACT_ADDRESS || undefined,
PRIVATE_KEY: process.env.PRIVATE_KEY || undefined,
},
SERPAPI_API_KEY: process.env.SERPAPI_API_KEY || '',
NODE_ENV: process.env.NODE_ENV || 'development',
RETRY_LIMIT: Number(process.env.RETRY_LIMIT) || 2,
};

const autoDriveConfig = {
AUTO_DRIVE_API_KEY: process.env.AUTO_DRIVE_API_KEY,
AUTO_DRIVE_ENCRYPTION_PASSWORD: process.env.AUTO_DRIVE_ENCRYPTION_PASSWORD,
AUTO_DRIVE_UPLOAD: process.env.AUTO_DRIVE_UPLOAD === 'true',
// SC Configuration
RPC_URL: process.env.RPC_URL,
CONTRACT_ADDRESS: process.env.CONTRACT_ADDRESS,
PRIVATE_KEY: process.env.PRIVATE_KEY,
WALLET_ADDRESS: process.env.WALLET_ADDRESS,
};
return configSchema.parse(rawConfig);
} catch (error) {
if (error instanceof z.ZodError) {
console.error('\x1b[31m%s\x1b[0m', formatZodError(error));
console.info(
'\x1b[36m%s\x1b[0m',
'\nTip: Copy .env.sample to .env and fill in the required values.',
);
process.exit(1);
}
throw error;
}
})();

export const config = {
twitterConfig,
llmConfig,
autoDriveConfig,

// Server Configuration
PORT: process.env.PORT || 3001,

// Environment
NODE_ENV: process.env.NODE_ENV || 'development',

// RESPONSE CONFIG
RETRY_LIMIT: process.env.RETRY_LIMIT || 2,
};
export type Config = z.infer<typeof configSchema>;
72 changes: 72 additions & 0 deletions auto-agents-framework/src/config/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { z } from 'zod';

const twitterConfigSchema = z.object({
USERNAME: z.string().min(1, 'Twitter username is required'),
PASSWORD: z.string().min(1, 'Twitter password is required'),
COOKIES_PATH: z.string(),
NUM_TIMELINE_TWEETS: z.number().int().positive(),
NUM_FOLLOWING_RECENT_TWEETS: z.number().int().positive(),
NUM_RANDOM_FOLLOWERS: z.number().int().positive(),
MAX_MENTIONS: z.number().int().positive(),
MAX_THREAD_LENGTH: z.number().int().positive(),
MAX_MY_RECENT_TWEETS: z.number().int().positive(),
POST_TWEETS: z.boolean(),
RESPONSE_INTERVAL_MS: z.number().int().positive(),
POST_INTERVAL_MS: z.number().int().positive(),
});

const llmConfigSchema = z.object({
LARGE_LLM_MODEL: z.string().min(1),
SMALL_LLM_MODEL: z.string().min(1),
OPENAI_API_KEY: z.string().min(1, 'OpenAI API key is required'),
});

const autoDriveConfigSchema = z.object({
AUTO_DRIVE_API_KEY: z.string().optional(),
AUTO_DRIVE_ENCRYPTION_PASSWORD: z.string().optional(),
AUTO_DRIVE_UPLOAD: z.boolean(),
});

const blockchainConfigSchema = z.object({
RPC_URL: z.string().optional(),
CONTRACT_ADDRESS: z.union([
z
.string()
.regex(
/^0x[0-9a-fA-F]{40}$/,
"Contract address must be a 42-character string: '0x' prefix followed by 40 hex characters",
)
.refine(
val => val.length === 42,
'Contract address must be exactly 42 characters (0x + 40 hex characters)',
),
z.literal(''),
z.literal(undefined),
]),
PRIVATE_KEY: z.union([
z
.string()
.regex(
/^0x[0-9a-fA-F]{64}$/,
"Private key must be a 66-character string: '0x' prefix followed by 64 hex characters",
)
.refine(
val => val.length === 66,
'Private key must be exactly 66 characters (0x + 64 hex characters)',
),
z.literal(''),
z.literal(undefined),
]),
});

const SERPAPI_API_KEY = z.string().optional();

export const configSchema = z.object({
twitterConfig: twitterConfigSchema,
llmConfig: llmConfigSchema,
autoDriveConfig: autoDriveConfigSchema,
blockchainConfig: blockchainConfigSchema,
SERPAPI_API_KEY: SERPAPI_API_KEY,
NODE_ENV: z.enum(['development', 'production', 'test']),
RETRY_LIMIT: z.number().int().nonnegative(),
});

0 comments on commit d542055

Please sign in to comment.