Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: (voice) enhance character card voice configuration support #698

Merged
merged 6 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "eliza",
"scripts": {
"preinstall": "npx only-allow pnpm",
"build": "turbo check-types build",
"build": "turbo run build --filter=@ai16z/plugin-bootstrap^... --filter=@ai16z/eliza^...",
"start": "pnpm --filter \"@ai16z/agent\" start --isRoot",
"start:client": "pnpm --dir client start --isRoot",
"start:debug": "cross-env NODE_ENV=development VERBOSE=true DEBUG=eliza:* pnpm --filter \"@ai16z/agent\" start --isRoot",
Expand Down Expand Up @@ -61,4 +61,4 @@
"workspaces": [
"packages/*"
]
}
}
45 changes: 33 additions & 12 deletions packages/adapter-postgres/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { v4 } from "uuid";

// Import the entire module as default
import pg from "pg";
type Pool = pg.Pool;
import postgres from "pg";
const { Pool } = postgres;
type PoolType = typeof postgres.Pool;

import {
QueryConfig,
Expand Down Expand Up @@ -32,10 +32,10 @@ const __filename = fileURLToPath(import.meta.url); // get the resolved path to t
const __dirname = path.dirname(__filename); // get the name of the directory

export class PostgresDatabaseAdapter
extends DatabaseAdapter<Pool>
extends DatabaseAdapter<PoolType>
implements IDatabaseCacheAdapter
{
private pool: Pool;
private pool: InstanceType<PoolType>;
private readonly maxRetries: number = 3;
private readonly baseDelay: number = 1000; // 1 second
private readonly maxDelay: number = 10000; // 10 seconds
Expand All @@ -51,7 +51,7 @@ export class PostgresDatabaseAdapter
connectionTimeoutMillis: this.connectionTimeout,
};

this.pool = new pg.Pool({
this.pool = new Pool({
...defaultConfig,
...connectionConfig, // Allow overriding defaults
});
Expand Down Expand Up @@ -137,7 +137,7 @@ export class PostgresDatabaseAdapter
await this.pool.end();

// Create new pool
this.pool = new pg.Pool({
this.pool = new Pool({
...this.pool.options,
connectionTimeoutMillis: this.connectionTimeout,
});
Expand Down 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
Loading