|
| 1 | +import { IAgentRuntime } from "@ai16z/eliza"; |
| 2 | +import { z } from "zod"; |
| 3 | + |
| 4 | +export const nodeEnvSchema = z.object({ |
| 5 | + OPENAI_API_KEY: z.string().min(1, "OpenAI API key is required"), |
| 6 | + |
| 7 | + // Core settings |
| 8 | + ELEVENLABS_XI_API_KEY: z.string().optional(), |
| 9 | + |
| 10 | + // All other settings optional with defaults |
| 11 | + ELEVENLABS_MODEL_ID: z.string().optional(), |
| 12 | + ELEVENLABS_VOICE_ID: z.string().optional(), |
| 13 | + ELEVENLABS_VOICE_STABILITY: z.string().optional(), |
| 14 | + ELEVENLABS_VOICE_SIMILARITY_BOOST: z.string().optional(), |
| 15 | + ELEVENLABS_VOICE_STYLE: z.string().optional(), |
| 16 | + ELEVENLABS_VOICE_USE_SPEAKER_BOOST: z.string().optional(), |
| 17 | + ELEVENLABS_OPTIMIZE_STREAMING_LATENCY: z.string().optional(), |
| 18 | + ELEVENLABS_OUTPUT_FORMAT: z.string().optional(), |
| 19 | + VITS_VOICE: z.string().optional(), |
| 20 | + VITS_MODEL: z.string().optional(), |
| 21 | +}); |
| 22 | + |
| 23 | +export type NodeConfig = z.infer<typeof nodeEnvSchema>; |
| 24 | + |
| 25 | +export async function validateNodeConfig( |
| 26 | + runtime: IAgentRuntime |
| 27 | +): Promise<NodeConfig> { |
| 28 | + try { |
| 29 | + const voiceSettings = runtime.character.settings?.voice; |
| 30 | + const elevenlabs = voiceSettings?.elevenlabs; |
| 31 | + |
| 32 | + // Only include what's absolutely required |
| 33 | + const config = { |
| 34 | + OPENAI_API_KEY: |
| 35 | + runtime.getSetting("OPENAI_API_KEY") || |
| 36 | + process.env.OPENAI_API_KEY, |
| 37 | + ELEVENLABS_XI_API_KEY: |
| 38 | + runtime.getSetting("ELEVENLABS_XI_API_KEY") || |
| 39 | + process.env.ELEVENLABS_XI_API_KEY, |
| 40 | + |
| 41 | + // Use character card settings first, fall back to env vars, then defaults |
| 42 | + ...(runtime.getSetting("ELEVENLABS_XI_API_KEY") && { |
| 43 | + ELEVENLABS_MODEL_ID: |
| 44 | + elevenlabs?.model || |
| 45 | + process.env.ELEVENLABS_MODEL_ID || |
| 46 | + "eleven_monolingual_v1", |
| 47 | + ELEVENLABS_VOICE_ID: |
| 48 | + elevenlabs?.voiceId || process.env.ELEVENLABS_VOICE_ID, |
| 49 | + ELEVENLABS_VOICE_STABILITY: |
| 50 | + elevenlabs?.stability || |
| 51 | + process.env.ELEVENLABS_VOICE_STABILITY || |
| 52 | + "0.5", |
| 53 | + ELEVENLABS_VOICE_SIMILARITY_BOOST: |
| 54 | + elevenlabs?.similarityBoost || |
| 55 | + process.env.ELEVENLABS_VOICE_SIMILARITY_BOOST || |
| 56 | + "0.75", |
| 57 | + ELEVENLABS_VOICE_STYLE: |
| 58 | + elevenlabs?.style || |
| 59 | + process.env.ELEVENLABS_VOICE_STYLE || |
| 60 | + "0", |
| 61 | + ELEVENLABS_VOICE_USE_SPEAKER_BOOST: |
| 62 | + elevenlabs?.useSpeakerBoost || |
| 63 | + process.env.ELEVENLABS_VOICE_USE_SPEAKER_BOOST || |
| 64 | + "true", |
| 65 | + ELEVENLABS_OPTIMIZE_STREAMING_LATENCY: |
| 66 | + process.env.ELEVENLABS_OPTIMIZE_STREAMING_LATENCY || "0", |
| 67 | + ELEVENLABS_OUTPUT_FORMAT: |
| 68 | + process.env.ELEVENLABS_OUTPUT_FORMAT || "pcm_16000", |
| 69 | + }), |
| 70 | + |
| 71 | + // VITS settings |
| 72 | + VITS_VOICE: voiceSettings?.model || process.env.VITS_VOICE, |
| 73 | + VITS_MODEL: process.env.VITS_MODEL, |
| 74 | + |
| 75 | + // AWS settings (only include if present) |
| 76 | + ...(runtime.getSetting("AWS_ACCESS_KEY_ID") && { |
| 77 | + AWS_ACCESS_KEY_ID: runtime.getSetting("AWS_ACCESS_KEY_ID"), |
| 78 | + AWS_SECRET_ACCESS_KEY: runtime.getSetting("AWS_SECRET_ACCESS_KEY"), |
| 79 | + AWS_REGION: runtime.getSetting("AWS_REGION"), |
| 80 | + AWS_S3_BUCKET: runtime.getSetting("AWS_S3_BUCKET"), |
| 81 | + AWS_S3_UPLOAD_PATH: runtime.getSetting("AWS_S3_UPLOAD_PATH"), |
| 82 | + }), |
| 83 | + }; |
| 84 | + |
| 85 | + return nodeEnvSchema.parse(config); |
| 86 | + } catch (error) { |
| 87 | + if (error instanceof z.ZodError) { |
| 88 | + const errorMessages = error.errors |
| 89 | + .map((err) => `${err.path.join(".")}: ${err.message}`) |
| 90 | + .join("\n"); |
| 91 | + throw new Error( |
| 92 | + `Node configuration validation failed:\n${errorMessages}` |
| 93 | + ); |
| 94 | + } |
| 95 | + throw error; |
| 96 | + } |
| 97 | +} |
0 commit comments