-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #97 from autonomys/ref/agent-frame-config
Add zod for config format - clean coding and refactoring
- Loading branch information
Showing
5 changed files
with
150 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | ||
}); |