Skip to content

Commit

Permalink
feat(voice): enhance character card voice configuration support
Browse files Browse the repository at this point in the history
- Add comprehensive ElevenLabs configuration in character cards
- Make ElevenLabs settings optional with sensible defaults
- Implement configuration priority (character card > env vars > defaults)
- Update schema validation to support optional fields
- Add debug logging for voice selection process
- Maintain backward compatibility with existing setups

Example character card config:
```json
{
    "settings": {
        "voice": {
            "model": "en_GB-alan-medium",
            "elevenlabs": {
                "voiceId": "your-voice-id",
                "model": "eleven_monolingual_v1",
                "stability": "0.5"
            }
        }
    }
}
```

Closes #694
  • Loading branch information
augchan42 committed Nov 30, 2024
1 parent 5f4c2d9 commit d46ab8f
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 162 deletions.
13 changes: 3 additions & 10 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,10 @@ export async function loadCharacters(
characterPaths,
cwd: process.cwd(),
dirname: __dirname,
fullPath: path.resolve(
process.cwd(),
"characters/8bitoracle.laozi.character.json"
),
exists: fs.existsSync(
path.resolve(
process.cwd(),
"characters/8bitoracle.laozi.character.json"
)
),
dirContents: fs.readdirSync(process.cwd()),
characters: fs
.readdirSync(path.join(process.cwd(), "characters"))
.filter((file) => file.endsWith(".character.json")),
});

if (characterPaths?.length > 0) {
Expand Down
31 changes: 26 additions & 5 deletions packages/adapter-postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,33 @@ export class PostgresDatabaseAdapter
async init() {
await this.testConnection();

const schema = fs.readFileSync(
path.resolve(__dirname, "../schema.sql"),
"utf8"
);
const client = await this.pool.connect();
try {
await client.query("BEGIN");

// Check if schema already exists (check for a core table)
const { rows } = await client.query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = 'rooms'
);
`);

if (!rows[0].exists) {
const schema = fs.readFileSync(
path.resolve(__dirname, "../schema.sql"),
"utf8"
);
await client.query(schema);
}

await this.query(schema);
await client.query("COMMIT");
} catch (error) {
await client.query("ROLLBACK");
throw error;
} finally {
client.release();
}
}

async close() {
Expand Down
13 changes: 11 additions & 2 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,8 +674,17 @@ export type Character = {
secrets?: { [key: string]: string };
buttplug?: boolean;
voice?: {
model?: string;
url?: string;
model?: string; // For VITS
url?: string; // Legacy VITS support
elevenlabs?: {
// New structured ElevenLabs config
voiceId: string;
model?: string;
stability?: string;
similarityBoost?: string;
style?: string;
useSpeakerBoost?: string;
};
};
model?: string;
embeddingModel?: string;
Expand Down
99 changes: 55 additions & 44 deletions packages/plugin-node/src/enviroment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,21 @@ import { z } from "zod";

export const nodeEnvSchema = z.object({
OPENAI_API_KEY: z.string().min(1, "OpenAI API key is required"),

// Core settings
ELEVENLABS_XI_API_KEY: z.string().optional(),
ELEVENLABS_MODEL_ID: z.string().min(1, "ElevenLabs model ID is required"),
ELEVENLABS_VOICE_ID: z.string().min(1, "ElevenLabs voice ID is required"),
ELEVENLABS_VOICE_STABILITY: z
.string()
.min(1, "ElevenLabs voice stability is required"),
ELEVENLABS_VOICE_SIMILARITY_BOOST: z
.string()
.min(1, "ElevenLabs voice similarity boost is required"),
ELEVENLABS_VOICE_STYLE: z
.string()
.min(1, "ElevenLabs voice style is required"),
ELEVENLABS_VOICE_USE_SPEAKER_BOOST: z
.string()
.min(1, "ElevenLabs voice speaker boost setting is required"),
ELEVENLABS_OPTIMIZE_STREAMING_LATENCY: z
.string()
.min(1, "ElevenLabs streaming latency optimization is required"),
ELEVENLABS_OUTPUT_FORMAT: z
.string()
.min(1, "ElevenLabs output format is required"),

// All other settings optional with defaults
ELEVENLABS_MODEL_ID: z.string().optional(),
ELEVENLABS_VOICE_ID: z.string().optional(),
ELEVENLABS_VOICE_STABILITY: z.string().optional(),
ELEVENLABS_VOICE_SIMILARITY_BOOST: z.string().optional(),
ELEVENLABS_VOICE_STYLE: z.string().optional(),
ELEVENLABS_VOICE_USE_SPEAKER_BOOST: z.string().optional(),
ELEVENLABS_OPTIMIZE_STREAMING_LATENCY: z.string().optional(),
ELEVENLABS_OUTPUT_FORMAT: z.string().optional(),
VITS_VOICE: z.string().optional(),
VITS_MODEL: z.string().optional(),
});

export type NodeConfig = z.infer<typeof nodeEnvSchema>;
Expand All @@ -32,34 +26,51 @@ export async function validateNodeConfig(
runtime: IAgentRuntime
): Promise<NodeConfig> {
try {
const voiceSettings = runtime.character.settings?.voice;
const elevenlabs = voiceSettings?.elevenlabs;

// Only include what's absolutely required
const config = {
OPENAI_API_KEY:
runtime.getSetting("OPENAI_API_KEY") ||
process.env.OPENAI_API_KEY,
ELEVENLABS_MODEL_ID:
runtime.getSetting("ELEVENLABS_MODEL_ID") ||
process.env.ELEVENLABS_MODEL_ID,
ELEVENLABS_VOICE_ID:
runtime.getSetting("ELEVENLABS_VOICE_ID") ||
process.env.ELEVENLABS_VOICE_ID,
ELEVENLABS_VOICE_STABILITY:
runtime.getSetting("ELEVENLABS_VOICE_STABILITY") ||
process.env.ELEVENLABS_VOICE_STABILITY,
ELEVENLABS_VOICE_SIMILARITY_BOOST:
runtime.getSetting("ELEVENLABS_VOICE_SIMILARITY_BOOST") ||
process.env.ELEVENLABS_VOICE_SIMILARITY_BOOST,
ELEVENLABS_VOICE_STYLE:
runtime.getSetting("ELEVENLABS_VOICE_STYLE") ||
process.env.ELEVENLABS_VOICE_STYLE,
ELEVENLABS_VOICE_USE_SPEAKER_BOOST:
runtime.getSetting("ELEVENLABS_VOICE_USE_SPEAKER_BOOST") ||
process.env.ELEVENLABS_VOICE_USE_SPEAKER_BOOST,
ELEVENLABS_OPTIMIZE_STREAMING_LATENCY:
runtime.getSetting("ELEVENLABS_OPTIMIZE_STREAMING_LATENCY") ||
process.env.ELEVENLABS_OPTIMIZE_STREAMING_LATENCY,
ELEVENLABS_OUTPUT_FORMAT:
runtime.getSetting("ELEVENLABS_OUTPUT_FORMAT") ||
process.env.ELEVENLABS_OUTPUT_FORMAT,
ELEVENLABS_XI_API_KEY:
runtime.getSetting("ELEVENLABS_XI_API_KEY") ||
process.env.ELEVENLABS_XI_API_KEY,

// Use character card settings first, fall back to env vars, then defaults
...(runtime.getSetting("ELEVENLABS_XI_API_KEY") && {
ELEVENLABS_MODEL_ID:
elevenlabs?.model ||
process.env.ELEVENLABS_MODEL_ID ||
"eleven_monolingual_v1",
ELEVENLABS_VOICE_ID:
elevenlabs?.voiceId || process.env.ELEVENLABS_VOICE_ID,
ELEVENLABS_VOICE_STABILITY:
elevenlabs?.stability ||
process.env.ELEVENLABS_VOICE_STABILITY ||
"0.5",
ELEVENLABS_VOICE_SIMILARITY_BOOST:
elevenlabs?.similarityBoost ||
process.env.ELEVENLABS_VOICE_SIMILARITY_BOOST ||
"0.75",
ELEVENLABS_VOICE_STYLE:
elevenlabs?.style ||
process.env.ELEVENLABS_VOICE_STYLE ||
"0",
ELEVENLABS_VOICE_USE_SPEAKER_BOOST:
elevenlabs?.useSpeakerBoost ||
process.env.ELEVENLABS_VOICE_USE_SPEAKER_BOOST ||
"true",
ELEVENLABS_OPTIMIZE_STREAMING_LATENCY:
process.env.ELEVENLABS_OPTIMIZE_STREAMING_LATENCY || "0",
ELEVENLABS_OUTPUT_FORMAT:
process.env.ELEVENLABS_OUTPUT_FORMAT || "pcm_16000",
}),

// VITS settings
VITS_VOICE: voiceSettings?.model || process.env.VITS_VOICE,
VITS_MODEL: process.env.VITS_MODEL,
};

return nodeEnvSchema.parse(config);
Expand Down
Loading

0 comments on commit d46ab8f

Please sign in to comment.