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

fix: .env Namespaced Character Secrets #410

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ characters/
packages/core/src/providers/cache
packages/core/src/providers/cache/*
cache/*

pnpm-lock.yaml
9 changes: 6 additions & 3 deletions docs/docs/guides/secrets-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ A comprehensive guide for managing secrets, API keys, and sensitive configuratio

Eliza uses a hierarchical environment variable system:

1. Character-specific secrets (highest priority)
2. Environment variables
3. Default values (lowest priority)
1. Character-specific namespaced environment variables (highest priority)
2. Character-specific secrets
3. Environment variables
4. Default values (lowest priority)

### Secret Types

Expand Down Expand Up @@ -89,6 +90,8 @@ Define secrets in character files:
}
```

Alternatively, you can use the `CHARACTER.YOUR_CHARACTER_NAME.SECRET_NAME` format inside your `.env` file.

Access secrets in code:

```typescript
Expand Down
14 changes: 14 additions & 0 deletions docs/docs/packages/agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ export async function initializeClients(

### Token Management

Tokens can be configured in two ways:

1. Using namespaced environment variables:
```env
CHARACTER.YOUR_CHARACTER_NAME.OPENAI_API_KEY=sk-...
CHARACTER.YOUR_CHARACTER_NAME.ANTHROPIC_API_KEY=sk-...
```

2. Using character settings:
```typescript
export function getTokenForProvider(
provider: ModelProviderName,
Expand All @@ -179,6 +188,11 @@ export function getTokenForProvider(
}
```

The system will check for tokens in the following order:
1. Character-specific namespaced env variables
2. Character settings from JSON
3. Global environment variables

### Database Selection

```typescript
Expand Down
26 changes: 20 additions & 6 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,25 @@ export async function loadCharacters(
for (const path of characterPaths) {
try {
const character = JSON.parse(fs.readFileSync(path, "utf8"));

const characterId = character.id || character.name;
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, '_')}.`;

const characterSettings = Object.entries(process.env)
.filter(([key]) => key.startsWith(characterPrefix))
.reduce((settings, [key, value]) => {
const settingKey = key.slice(characterPrefix.length);
return { ...settings, [settingKey]: value };
}, {});

if (Object.keys(characterSettings).length > 0) {
character.settings = character.settings || {};
character.settings.secrets = {
...characterSettings,
...character.settings.secrets
};
}

// is there a "plugins" field?
if (character.plugins) {
console.log("Plugins are: ", character.plugins);

Expand All @@ -99,13 +116,10 @@ export async function loadCharacters(
}
}
}

if (loadedCharacters.length === 0) {
loadedCharacters.length === 0 &&
console.log("No characters found, using default character");
loadedCharacters.push(defaultCharacter);
}

return loadedCharacters;
return loadedCharacters.length > 0 ? loadedCharacters : [defaultCharacter];
}

export function getTokenForProvider(
Expand Down
31 changes: 31 additions & 0 deletions packages/core/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ interface Settings {
[key: string]: string | undefined;
}

interface NamespacedSettings {
[namespace: string]: Settings;
}

let environmentSettings: Settings = {};

/**
Expand Down Expand Up @@ -81,6 +85,15 @@ export function loadEnvConfig(): Settings {
}

console.log(`Loaded .env file from: ${envPath}`);

// Parse namespaced settings
const namespacedSettings = parseNamespacedSettings(process.env as Settings);

// Attach to process.env for backward compatibility
Object.entries(namespacedSettings).forEach(([namespace, settings]) => {
process.env[`__namespaced_${namespace}`] = JSON.stringify(settings);
});

return process.env as Settings;
}

Expand Down Expand Up @@ -115,3 +128,21 @@ export function hasEnvVariable(key: string): boolean {
// Initialize settings based on environment
export const settings = isBrowser() ? environmentSettings : loadEnvConfig();
export default settings;

// Add this function to parse namespaced settings
function parseNamespacedSettings(env: Settings): NamespacedSettings {
const namespaced: NamespacedSettings = {};

for (const [key, value] of Object.entries(env)) {
if (!value) continue;

const [namespace, ...rest] = key.split('.');
if (!namespace || rest.length === 0) continue;

const settingKey = rest.join('.');
namespaced[namespace] = namespaced[namespace] || {};
namespaced[namespace][settingKey] = value;
}

return namespaced;
}
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading