@botpress/adk-cli
Version:
Command-line interface for the Botpress Agent Development Kit (ADK)
495 lines (379 loc) • 12.8 kB
Markdown
# Configuration
Configure your bot's behavior, integration aliases, and global state through the unified `agent.config.ts` file. Installed integration and plugin state is managed through Botpress Cloud dependency snapshots, not through `agent.config.ts`.
## agent.config.ts
The main configuration file defines your bot's core settings, AI models, integration aliases, and state schemas.
### Configuration Example
```typescript
import { defineConfig, z } from '@botpress/runtime'
export default defineConfig({
name: 'customer-support-bot',
description: 'Customer support assistant with knowledge base',
// AI Model Configuration
defaultModels: {
autonomous: 'openai:gpt-4o', // For execute() function
zai: 'openai:gpt-4o-mini', // For zai operations
},
// User State Schema
user: {
state: z.object({
preferredLanguage: z.enum(['en', 'es', 'fr', 'de']).default('en'),
timezone: z.string().default('UTC'),
name: z.string().optional(),
email: z.string().email().optional(),
notificationsEnabled: z.boolean().default(true),
accountTier: z.enum(['free', 'pro', 'enterprise']).default('free'),
metadata: z.object({}).passthrough().default({}),
}),
},
// Bot Global State Schema
bot: {
state: z.object({
version: z.number().default(1),
maintenanceMode: z.boolean().default(false),
// Feature flags
features: z
.object({
advancedSearch: z.boolean().default(false),
multiLanguage: z.boolean().default(true),
})
.default({}),
// Analytics
totalConversations: z.number().default(0),
totalUsers: z.number().default(0),
}),
},
secrets: {
OPENAI_API_KEY: {
description: 'OpenAI API key used by custom model calls',
},
OPTIONAL_WEBHOOK_SECRET: {
optional: true,
description: 'Shared secret for optional webhook verification',
},
},
// Cloud is the source of truth; .adk/dependencies/ stores generated snapshots.
// Use `adk integrations add/remove/configure` to manage them — see integrations.md
})
```
### Additional Configuration Fields
#### Tags
Tags are key-value pairs for categorizing entities. All entities (`user`, `bot`, `conversation`, `message`, `workflow`) support tags. See **[Tags](./tags.md)** for complete documentation.
#### Bot Configuration Schema
Define a custom configuration schema for bot-level settings accessible via the Context API:
```typescript
export default defineConfig({
name: 'my-bot',
configuration: {
schema: z.object({
maxRetries: z.number().default(3),
apiEndpoint: z.string(),
featureFlags: z.object({
enableBetaFeatures: z.boolean().default(false),
}),
}),
},
})
```
Access via direct import from `@botpress/runtime`:
```typescript
import { configuration } from '@botpress/runtime'
if (configuration.featureFlags.enableBetaFeatures) {
// Use beta features
}
```
See **[Context API](./context-api.md)** for details on accessing other runtime values.
#### Secrets
Declare secret names in `agent.config.ts`, then read values at runtime with the typed `secrets` proxy:
```typescript
import { secrets } from '@botpress/runtime'
const apiKey = secrets.OPENAI_API_KEY
```
Secret names must be `SCREAMING_SNAKE_CASE` and cannot use reserved prefixes such as `SECRET_`, `BP_`, or `BOTPRESS_`. Values are set separately with `adk secret:set <KEY> <value>` for dev or `adk secret:set <KEY> <value> --prod` for prod. Dev values live in `.adk/secrets.json`. Prod values live on the remote bot and are write-only; ADK can show set/unset status, but cannot read values back.
### Model Configuration
**Default Models:**
If you don't specify `defaultModels`, the ADK uses these defaults:
- `zai`: `"openai:gpt-4.1-2025-04-14"`
- `autonomous`: `"openai:gpt-4.1-mini-2025-04-14"`
**Available Models:**
```typescript
// OpenAI
'openai:gpt-4o'
'openai:gpt-4o-mini'
'openai:gpt-4-turbo'
'openai:gpt-4.1-2025-04-14'
'openai:gpt-4.1-mini-2025-04-14'
// Anthropic
'anthropic:claude-3-5-sonnet'
'anthropic:claude-3-opus'
'anthropic:claude-3-haiku'
// Google
'google:gemini-1.5-pro'
'google:gemini-1.5-flash'
```
**Model Fallback Arrays:**
You can specify multiple models as fallbacks:
```typescript
defaultModels: {
autonomous: [
"openai:gpt-4o",
"anthropic:claude-3-5-sonnet",
"openai:gpt-4o-mini"
],
zai: "openai:gpt-4o-mini"
}
```
### Accessing Configuration
```typescript
import { bot, user } from '@botpress/runtime'
// In any handler (action, workflow, conversation)
// Access bot state
const version = bot.state.version
const maintenanceMode = bot.state.maintenanceMode
// Modify bot state
bot.state.totalConversations += 1
bot.state.features.advancedSearch = true
// Access user state
const language = user.state.preferredLanguage
const tier = user.state.accountTier
// Modify user state
user.state.lastActiveDate = new Date()
user.state.metadata.lastQuery = 'product pricing'
```
## Dependencies (Integrations, Plugins, Interfaces)
Integration and plugin state lives in Botpress Cloud, not in `agent.config.ts`. The ADK writes generated per-environment snapshots under `.adk/dependencies/` for fast/offline reads:
- `.adk/dependencies/dev.json` — development environment snapshot
- `.adk/dependencies/prod.json` — production environment snapshot
- `.adk/dependencies/migration.json` — one-way legacy migration marker
Cloud is the source of truth. Snapshots are refreshed after dependency mutations and Cloud reads. Never edit dependency snapshots by hand.
> **Migration:** Projects with a legacy `dependencies` block in `agent.config.ts` or legacy `dependencies.<env>.lock.json` files are auto-migrated on the first CLI command. If the target Cloud bot has no dependency state, legacy state is imported to Cloud automatically, including prod. The migration is one-shot and is skipped whenever `.adk/dependencies/migration.json` exists; the marker contents are informational, so even a corrupt marker still counts as migrated.
Use `adk dependencies export` / `adk dependencies import` only for explicit dependency-only backup or transfer artifacts. These JSON files are not the generated local snapshots and should not become a new source of truth.
### Managing Dependencies
Use the `adk integrations` CLI subcommands. See **[CLI Reference](./cli.md)** and **[Integrations](./integrations.md)** for full details:
```bash
adk integrations search <query> # Search for integrations
adk integrations add <name>@<version> # Add integration
adk integrations configure <alias> --set key=value # Configure
adk integrations enable <alias> # Enable
adk integrations remove <alias> # Remove
adk integrations upgrade <alias> # Upgrade
adk integrations list # List installed
adk integrations status # Show availability/remediation
```
### Using Integration Actions
See **[Integration Actions](./integration-actions.md)** for calling integration functionality from your code.
## Environment Variables
### .env File
```bash
# Bot Configuration
BOT_NAME=customer-support-bot
NODE_ENV=development
# API Keys
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
# Integration Tokens
SLACK_BOT_TOKEN=xoxb-...
LINEAR_API_KEY=lin_api_...
# ADK Development (CLI-specific)
ADK_DEV_PORT=3000 # Bot port (default: 3000)
ADK_CONSOLE_PORT=3001 # UI console port (default: 3001)
DEBUG=adk:* # Enable ADK debug logs
# Feature Flags (optional)
ENABLE_ADVANCED_SEARCH=true
# External Services (optional)
WEBHOOK_URL=https://api.example.com/webhooks
```
### Accessing Environment Variables
```typescript
// In configuration
export default defineConfig({
name: process.env.BOT_NAME || 'my-bot',
defaultModels: {
autonomous: process.env.AI_MODEL || 'openai:gpt-4o',
},
})
// In handlers
export const myAction = new Action({
async handler({ input }) {
const apiKey = process.env.EXTERNAL_API_KEY
if (!apiKey) {
throw new Error('EXTERNAL_API_KEY not configured')
}
// Use the API key
const response = await fetch('https://api.example.com', {
headers: { Authorization: `Bearer ${apiKey}` },
})
},
})
```
## State Management Patterns
### User State Patterns
```typescript
// Progressive profile building
export const Chat = new Conversation({
async handler({ message, conversation, execute }) {
// Collect user info progressively
if (!user.state.name && message?.type === 'text') {
user.state.name = extractName(message.payload.text)
}
if (!user.state.email) {
// Ask for email if needed
await conversation.send({
type: 'text',
payload: { text: "What's your email address?" },
})
}
// Use preferences
const language = user.state.preferredLanguage
await execute({
instructions: `Respond in ${language}`,
})
},
})
```
### Bot State Patterns
```typescript
export const Chat = new Conversation({
async handler({ conversation }) {
// Feature flag checking
if (bot.state.features.advancedSearch) {
// Use advanced search
const results = await advancedSearch(query)
} else {
// Use basic search
const results = await basicSearch(query)
}
// Maintenance mode
if (bot.state.maintenanceMode) {
await conversation.send({
type: 'text',
payload: {
text: 'The bot is currently under maintenance. Please try again later.',
},
})
return
}
// Business hours check (example)
const supportHours = '9:00 AM to 5:00 PM EST'
await conversation.send({
type: 'text',
payload: { text: `Our support hours are ${supportHours}` },
})
},
})
```
## Best Practices
### 1. Use Declared Secrets for Runtime Credentials
```typescript
// ❌ Bad - hardcoded secrets
config: {
apiKey: 'sk-abc123def456'
}
// ✅ Good - declared in agent.config.ts
secrets: {
OPENAI_API_KEY: {
description: 'OpenAI API key'
}
}
// ✅ Good - read in runtime code
import { secrets } from '@botpress/runtime'
const apiKey = secrets.OPENAI_API_KEY
```
Use `${env:VAR_NAME}` only for integration or plugin configuration values that the CLI applies to Cloud. For credentials read by bot code, declare a secret and access it through `secrets.KEY`.
### 2. Validate Configuration
```typescript
export default defineConfig({
name: validateBotName(process.env.BOT_NAME),
user: {
state: z.object({
// Use strict validation
email: z.string().email(),
age: z.number().int().min(0).max(150),
}),
},
})
```
### 3. Provide Defaults
```typescript
user: {
state: z.object({
// Always provide sensible defaults
language: z.string().default('en'),
notifications: z.boolean().default(true),
theme: z.enum(['light', 'dark']).default('light'),
})
}
```
### 4. Document State Schema
```typescript
bot: {
state: z.object({
/**
* Current version of the bot configuration
* Increment when making breaking changes
*/
version: z.number().default(1),
/**
* Feature flags for gradual rollout
* @example { "newUI": true, "betaFeatures": false }
*/
features: z.record(z.boolean()).default({}),
})
}
```
### 5. Separate Concerns
```typescript
// Separate configuration by domain
const userConfig = {
state: userStateSchema,
}
const botConfig = {
state: botStateSchema,
}
const integrationConfig = {
slack: slackConfig,
discord: discordConfig,
}
export default defineConfig({
...baseConfig,
user: userConfig,
bot: botConfig,
})
```
## Project Files
### agent.json
The `agent.json` file stores the shared production bot and workspace IDs for deployment. This file is created by `adk link`.
```json
{
"botId": "bot_abc123",
"workspaceId": "ws_xyz789"
}
```
**Fields:**
- `botId` - Production bot ID (used by `adk deploy`)
- `workspaceId` - Workspace ID
The development bot ID is stored in `agent.local.json`:
```json
{
"devId": "bot_dev_123"
}
```
**Important:**
- Add `agent.json` to `.gitignore` if you do not want environment-specific IDs committed
- Each developer/environment can have a different local `devId`
- `agent.json` is created by `adk link`; `agent.local.json` is written by local dev flows
- Current scaffolds do not add `agent.json` to `.gitignore` automatically
### package.json
Standard Node.js package file with ADK-specific scripts.
```json
{
"scripts": {
"dev": "adk dev",
"build": "adk build",
"deploy": "adk deploy"
},
"dependencies": {
"@botpress/runtime": "workspace:*"
}
}
```
See **[CLI Reference](./cli.md)** for complete command documentation.