diff --git a/docs/config-versioning.md b/docs/config-versioning.md index 36d7fdd25..b5cdaf990 100644 --- a/docs/config-versioning.md +++ b/docs/config-versioning.md @@ -11,24 +11,35 @@ PicoClaw uses a schema versioning system for `config.json` to ensure smooth upgr - **Changes**: Added `version` field to Config struct - **Migration**: No structural changes needed for existing configs +### Version 2 +- **Introduction**: Model enable/disable support and channel config unification +- **Changes**: + - Added `enabled` field to `ModelConfig` — allows disabling individual model entries without removing them + - During V1→V2 migration, `enabled` is auto-inferred: models with API keys or the reserved `local-model` name are enabled; others default to disabled + - Migrated legacy channel fields: Discord `mention_only` → `group_trigger.mention_only`, OneBot `group_trigger_prefix` → `group_trigger.prefixes` + - V0 configs now migrate directly to CurrentVersion (V2) instead of going through V1 + - `makeBackup()` now uses date-only suffix (e.g., `config.json.20260330.bak`) and also backs up `.security.yml` + ## How It Works ### Automatic Migration When you load a config file: 1. The system first reads the `version` field from the JSON -2. Based on the detected version, it loads the appropriate config struct (`ConfigV0`, `ConfigV1`, etc.) +2. Based on the detected version, it loads the appropriate config struct (`configV0`, `configV1`, etc.) 3. If the loaded version is less than the latest, migrations are applied incrementally -4. The version number is updated automatically -5. The migrated config is automatically saved back to disk +4. Before saving, the system automatically creates a date-stamped backup of `config.json` and `.security.yml` +5. The version number is updated automatically +6. The migrated config is automatically saved back to disk ### Version Field The `version` field in `config.json` indicates the schema version: - `0` or missing: Legacy config (no version field) -- `1`: Current version with versioning support +- `1`: Previous version (will be auto-migrated to V2 on load) +- `2`: Current version ```json { - "version": 1, + "version": 2, "agents": {...}, ... } @@ -54,25 +65,25 @@ type ConfigV2 struct { ### Step 2: Update Current Config Version ```go -const CurrentConfigVersion = 2 // Increment this +const CurrentVersion = 2 // Increment this ``` ### Step 3: Add a Loader Function ```go -// loadConfigV2 loads a version 2 config -func loadConfigV2(data []byte) (*Config, error) { +// loadConfigV3 loads a version 3 config +func loadConfigV3(data []byte) (*Config, error) { cfg := DefaultConfig() - // Parse to ConfigV2 struct - var v2 ConfigV2 - if err := json.Unmarshal(data, &v2); err != nil { + // Parse to ConfigV3 struct + var v3 ConfigV3 + if err := json.Unmarshal(data, &v3); err != nil { return nil, err } // Convert to current Config - cfg.Version = v2.Version - cfg.Agents = v2.Agents + cfg.Version = v3.Version + cfg.Agents = v3.Agents // ... map other fields return cfg, nil @@ -82,29 +93,12 @@ func loadConfigV2(data []byte) (*Config, error) { ### Step 4: Add Migration Logic ```go -// applyMigration applies a single migration step from fromVersion to toVersion -func applyMigration(cfg *Config, fromVersion, toVersion int) (*Config, error) { - switch toVersion { - case 1: - // Migration from version 0 to 1 - return &Config{ - Version: 1, - Agents: cfg.Agents, - // ... copy all fields - }, nil - case 2: - // Migration from version 1 to 2 - // Example: Move or rename fields - migrated := *cfg - migrated.Version = 2 - // Apply structural changes - if cfg.SomeOldField != "" { - migrated.SomeNewField = cfg.SomeOldField - } - return &migrated, nil - default: - return nil, fmt.Errorf("unsupported migration target version: %d", toVersion) - } +func (c *configV2) Migrate() (*Config, error) { + // Apply V2→V3 structural changes here + migrated := &c.Config + migrated.Version = 3 + // Apply structural changes + return migrated, nil } ``` @@ -120,7 +114,9 @@ func LoadConfig(path string) (*Config, error) { case 1: cfg, err = loadConfigV1(data) case 2: - cfg, err = loadConfigV2(data) + cfg, err = loadConfig(data) + case 3: + cfg, err = loadConfigV3(data) default: return nil, fmt.Errorf("unsupported config version: %d", versionInfo.Version) } @@ -134,22 +130,22 @@ func LoadConfig(path string) (*Config, error) { Create a test in `config_migration_test.go`: ```go -func TestMigrateV1ToV2(t *testing.T) { - // Create a version 1 config - v1Config := Config{ - Version: 1, +func TestMigrateV2ToV3(t *testing.T) { + // Create a version 2 config + v2Config := Config{ + Version: 2, // ... set up test data } // Apply migration - migrated, err := applyMigration(&v1Config, 1, 2) + migrated, err := v2Config.Migrate() if err != nil { t.Fatalf("Migration failed: %v", err) } // Verify version is updated - if migrated.Version != 2 { - t.Errorf("Expected version 2, got %d", migrated.Version) + if migrated.Version != 3 { + t.Errorf("Expected version 3, got %d", migrated.Version) } // Verify data is preserved/transformed correctly @@ -164,58 +160,60 @@ func TestMigrateV1ToV2(t *testing.T) { 3. **No Data Loss**: Migrations should preserve all user settings 4. **Idempotent**: Running the same migration multiple times should be safe 5. **Auto-Save**: Migrated configs are automatically saved to update the user's file -6. **Test Thoroughly**: Test with real user config files -7. **Update Defaults**: Keep `defaults.go` in sync with the latest schema +6. **Auto-Backup**: Before saving, the system creates a date-stamped backup of `config.json` and `.security.yml` +7. **Test Thoroughly**: Test with real user config files +8. **Update Defaults**: Keep `defaults.go` in sync with the latest schema ## Example Migration ### Scenario: Adding a new field with default value -Old config (version 1): -```json -{ - "version": 1, - "agents": { - "defaults": { - "max_tokens": 32768 - } - } -} -``` - -Migration to version 2: -```go -case 2: - migrated := *cfg - migrated.Version = 2 - - // Add new field with default value if not set - if migrated.Agents.Defaults.NewFeatureEnabled == false { - // Use default value - } - - return &migrated, nil -``` - -New config (version 2): +Old config (version 2): ```json { "version": 2, - "agents": { - "defaults": { - "max_tokens": 32768, - "new_feature_enabled": false + "model_list": [ + { + "model_name": "gpt-5.4", + "model": "openai/gpt-5.4" } - } + ] +} +``` + +Migration to version 3: +```go +func (c *configV2) Migrate() (*Config, error) { + migrated := &c.Config + migrated.Version = 3 + + // Add new field with default value if not set + // ... + + return migrated, nil +} +``` + +New config (version 3): +```json +{ + "version": 3, + "model_list": [ + { + "model_name": "gpt-5.4", + "model": "openai/gpt-5.4", + "new_option": true + } + ] } ``` ## Troubleshooting ### Config Not Upgrading -- Check that `CurrentConfigVersion` is incremented -- Verify migration logic in `applyMigration()` handles the target version -- Ensure `migrateConfig()` is called in `LoadConfig()` +- Check that `CurrentVersion` is incremented +- Verify migration logic handles the target version +- Ensure `Migrate()` is called in `LoadConfig()` ### Migration Errors - Check error messages for specific migration failures @@ -227,4 +225,5 @@ New config (version 2): - Ensure all fields are copied during migration - Check that the migration doesn't overwrite values with defaults unnecessarily - Review the conversion logic in the loader functions +- Check the auto-backup files (e.g., `config.json.20260330.bak`) to recover original data diff --git a/docs/configuration.md b/docs/configuration.md index 9b9524692..560082e0e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -479,8 +479,9 @@ This design also enables **multi-agent support** with flexible provider selectio - **Different agents, different providers**: Each agent can use its own LLM provider - **Model fallbacks**: Configure primary and fallback models for resilience -- **Load balancing**: Distribute requests across multiple endpoints +- **Load balancing**: Distribute requests across multiple endpoints or keys - **Centralized configuration**: Manage all providers in one place +- **Model enable/disable**: Use the `enabled` field to temporarily disable a model without removing its configuration #### 🔒 Security Configuration (Recommended) @@ -581,22 +582,22 @@ For complete documentation, see [`security_configuration.md`](security_configura { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -607,7 +608,9 @@ For complete documentation, see [`security_configuration.md`](security_configura } ``` -> **Security Note**: You can remove `api_key` fields from your config and store them in `.security.yml` instead. See [Security Configuration](#-security-configuration-recommended) above for details. +> **Security Note**: You can remove `api_keys` fields from your config and store them in `.security.yml` instead. See [Security Configuration](#-security-configuration-recommended) above for details. +> +> **Note**: The `enabled` field can be set to `false` to disable a model entry without removing it. When omitted, it defaults to `true` during migration for models that have API keys. #### Vendor-Specific Examples @@ -684,7 +687,7 @@ For direct Anthropic API access or custom endpoints that only support Anthropic' { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -759,13 +762,13 @@ model_list: "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -773,7 +776,7 @@ model_list: #### Migration from Legacy `providers` Config -The old `providers` configuration is **deprecated** but still supported for backward compatibility. See [docs/migration/model-list-migration.md](../migration/model-list-migration.md) for the full guide. +The old `providers` configuration is **deprecated** and has been removed in V2. Existing V0/V1 configs are auto-migrated. See [docs/migration/model-list-migration.md](../migration/model-list-migration.md) for the full guide. ### Provider Architecture @@ -783,7 +786,7 @@ PicoClaw routes providers by protocol family: - **Anthropic**: Claude-native API behavior. - **Codex/OAuth**: OpenAI OAuth/token authentication route. -This keeps the runtime lightweight while making new OpenAI-compatible backends mostly a config operation (`api_base` + `api_key`). +This keeps the runtime lightweight while making new OpenAI-compatible backends mostly a config operation (`api_base` + `api_keys`).
Zhipu (legacy providers format) diff --git a/docs/credential_encryption.md b/docs/credential_encryption.md index de3b70e09..54c2ee5f9 100644 --- a/docs/credential_encryption.md +++ b/docs/credential_encryption.md @@ -1,6 +1,6 @@ # Credential Encryption -PicoClaw supports encrypting `api_key` values in `model_list` configuration entries. +PicoClaw supports encrypting `api_key`/`api_keys` values in `model_list` configuration entries. Encrypted keys are stored as `enc://` strings and decrypted automatically at startup. --- @@ -42,6 +42,8 @@ enc://AAAA...base64... ## Supported `api_key` Formats +The same formats apply to both `api_key` (singular) and individual elements in the `api_keys` (array) field: + | Format | Example | Behaviour | |--------|---------|-----------| | Plaintext | `sk-abc123` | Used as-is | diff --git a/docs/docker.md b/docs/docker.md index 18de1ee83..6c32879a6 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -95,19 +95,19 @@ picoclaw onboard { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key", + "api_keys": ["sk-your-api-key"], "api_base":"https://ark.cn-beijing.volces.com/api/coding/v3" }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "request_timeout": 300 }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "your-anthropic-key" + "api_keys": ["your-anthropic-key"] } ], "tools": { diff --git a/docs/fr/configuration.md b/docs/fr/configuration.md index fd8602c68..7a57cceae 100644 --- a/docs/fr/configuration.md +++ b/docs/fr/configuration.md @@ -334,15 +334,15 @@ Configurez plusieurs endpoints pour le même nom de modèle — PicoClaw effectu ```json { "model_list": [ - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_key": "sk-key1" }, - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_key": "sk-key2" } + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_keys": ["sk-key1"] }, + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_keys": ["sk-key2"] } ] } ``` #### Migration depuis l'ancienne config `providers` -L'ancienne configuration `providers` est **dépréciée** mais toujours supportée. Voir [docs/migration/model-list-migration.md](../migration/model-list-migration.md). +L'ancienne configuration `providers` est **dépréciée** et a été supprimée dans V2. Les configs V0/V1 existantes sont auto-migrées. Voir [docs/migration/model-list-migration.md](../migration/model-list-migration.md). ### Architecture des Providers diff --git a/docs/fr/docker.md b/docs/fr/docker.md index 432edb1b2..9605440bc 100644 --- a/docs/fr/docker.md +++ b/docs/fr/docker.md @@ -92,19 +92,19 @@ picoclaw onboard { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key", + "api_keys": ["sk-your-api-key"], "api_base":"https://ark.cn-beijing.volces.com/api/coding/v3" }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "request_timeout": 300 }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "your-anthropic-key" + "api_keys": ["your-anthropic-key"] } ], "tools": { diff --git a/docs/fr/providers.md b/docs/fr/providers.md index 39f5cf36a..d0da81897 100644 --- a/docs/fr/providers.md +++ b/docs/fr/providers.md @@ -73,22 +73,22 @@ Cette conception permet également le **support multi-agents** avec une sélecti { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -107,7 +107,7 @@ Cette conception permet également le **support multi-agents** avec une sélecti { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -117,7 +117,7 @@ Cette conception permet également le **support multi-agents** avec une sélecti { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -127,7 +127,7 @@ Cette conception permet également le **support multi-agents** avec une sélecti { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ``` @@ -137,7 +137,7 @@ Cette conception permet également le **support multi-agents** avec une sélecti { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -147,7 +147,7 @@ Cette conception permet également le **support multi-agents** avec une sélecti { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] } ``` @@ -161,7 +161,7 @@ Pour l'accès direct à l'API Anthropic ou les endpoints personnalisés qui ne p { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -189,7 +189,7 @@ Pour l'accès direct à l'API Anthropic ou les endpoints personnalisés qui ne p "model_name": "my-custom-model", "model": "openai/custom-model", "api_base": "https://my-proxy.com/v1", - "api_key": "sk-...", + "api_keys": ["sk-..."], "request_timeout": 300 } ``` @@ -201,7 +201,7 @@ Pour l'accès direct à l'API Anthropic ou les endpoints personnalisés qui ne p "model_name": "lite-gpt4", "model": "litellm/lite-gpt4", "api_base": "http://localhost:4000/v1", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -218,13 +218,13 @@ Configurez plusieurs endpoints pour le même nom de modèle — PicoClaw effectu "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -232,7 +232,7 @@ Configurez plusieurs endpoints pour le même nom de modèle — PicoClaw effectu #### Migration depuis l'Ancienne Configuration `providers` -L'ancienne configuration `providers` est **dépréciée** mais toujours prise en charge pour la compatibilité ascendante. +L'ancienne configuration `providers` est **dépréciée** et a été supprimée dans V2. Les configs V0/V1 existantes sont auto-migrées. **Ancienne configuration (dépréciée) :** @@ -257,11 +257,12 @@ L'ancienne configuration `providers` est **dépréciée** mais toujours prise en ```json { + "version": 2, "model_list": [ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ], "agents": { diff --git a/docs/ja/configuration.md b/docs/ja/configuration.md index da60b6052..6d6290e8a 100644 --- a/docs/ja/configuration.md +++ b/docs/ja/configuration.md @@ -335,15 +335,15 @@ HEARTBEAT_OK を返信 ユーザーが直接結果を受信 ```json { "model_list": [ - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_key": "sk-key1" }, - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_key": "sk-key2" } + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_keys": ["sk-key1"] }, + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_keys": ["sk-key2"] } ] } ``` #### 旧 `providers` 設定からの移行 -旧 `providers` 設定は**非推奨**ですが後方互換性のためサポートされています。[docs/migration/model-list-migration.md](../migration/model-list-migration.md) を参照してください。 +旧 `providers` 設定は**非推奨**となり、V2 で削除されました。既存の V0/V1 設定は自動的に移行されます。[docs/migration/model-list-migration.md](../migration/model-list-migration.md) を参照してください。 ### Provider アーキテクチャ diff --git a/docs/ja/docker.md b/docs/ja/docker.md index 31ed17ec5..a585c5e80 100644 --- a/docs/ja/docker.md +++ b/docs/ja/docker.md @@ -94,19 +94,19 @@ picoclaw onboard { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key", + "api_keys": ["sk-your-api-key"], "api_base":"https://ark.cn-beijing.volces.com/api/coding/v3" }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "request_timeout": 300 }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "your-anthropic-key" + "api_keys": ["your-anthropic-key"] } ], "tools": { diff --git a/docs/ja/providers.md b/docs/ja/providers.md index 9a53a4b69..e29c113f3 100644 --- a/docs/ja/providers.md +++ b/docs/ja/providers.md @@ -73,22 +73,22 @@ { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -107,7 +107,7 @@ { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -117,7 +117,7 @@ { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -127,7 +127,18 @@ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] +} +``` + +**LiteLLM Proxy** + +```json +{ + "model_name": "lite-gpt4", + "model": "litellm/lite-gpt4", + "api_base": "http://localhost:4000/v1", + "api_keys": ["sk-..."] } ``` @@ -137,7 +148,7 @@ { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -147,7 +158,7 @@ { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] } ``` @@ -161,7 +172,7 @@ Anthropic API への直接アクセスや、Anthropic のネイティブメッ { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -189,7 +200,7 @@ Anthropic API への直接アクセスや、Anthropic のネイティブメッ "model_name": "my-custom-model", "model": "openai/custom-model", "api_base": "https://my-proxy.com/v1", - "api_key": "sk-...", + "api_keys": ["sk-..."], "request_timeout": 300 } ``` @@ -201,7 +212,7 @@ Anthropic API への直接アクセスや、Anthropic のネイティブメッ "model_name": "lite-gpt4", "model": "litellm/lite-gpt4", "api_base": "http://localhost:4000/v1", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -218,13 +229,13 @@ PicoClaw はリクエスト送信前に外側の `litellm/` プレフィック "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -232,7 +243,7 @@ PicoClaw はリクエスト送信前に外側の `litellm/` プレフィック #### レガシー `providers` 設定からの移行 -旧 `providers` 設定形式は**非推奨**ですが、後方互換性のためまだサポートされています。 +旧 `providers` 設定形式は**非推奨**となり、V2 で削除されました。既存の V0/V1 設定は自動的に移行されます。 **旧設定(非推奨):** @@ -257,11 +268,12 @@ PicoClaw はリクエスト送信前に外側の `litellm/` プレフィック ```json { + "version": 2, "model_list": [ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ], "agents": { @@ -282,7 +294,7 @@ PicoClaw はプロトコルファミリーごとに Provider をルーティン - Anthropic プロトコル:Claude ネイティブ API 動作。 - Codex/OAuth パス:OpenAI OAuth/Token 認証ルート。 -これによりランタイムを軽量に保ちつつ、新しい OpenAI 互換バックエンドの追加をほぼ設定操作(`api_base` + `api_key`)のみで実現しています。 +これによりランタイムを軽量に保ちつつ、新しい OpenAI 互換バックエンドの追加をほぼ設定操作(`api_base` + `api_keys`)のみで実現しています。
Zhipu 設定例 diff --git a/docs/migration/model-list-migration.md b/docs/migration/model-list-migration.md index 9d05ac599..f2a545f8f 100644 --- a/docs/migration/model-list-migration.md +++ b/docs/migration/model-list-migration.md @@ -50,22 +50,23 @@ The new `model_list` configuration offers several advantages: ```json { + "version": 2, "model_list": [ { "model_name": "gpt4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key", + "api_keys": ["sk-your-openai-key"], "api_base": "https://api.openai.com/v1" }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "deepseek", "model": "deepseek/deepseek-chat", - "api_key": "sk-your-deepseek-key" + "api_keys": ["sk-your-deepseek-key"] } ], "agents": { @@ -76,6 +77,8 @@ The new `model_list` configuration offers several advantages: } ``` +> **Note**: The `enabled` field can be omitted — during V1→V2 migration it is auto-inferred (models with API keys or the `local-model` name are enabled by default). For new configs, you can explicitly set `"enabled": false` to disable a model entry without removing it. + ## Protocol Prefixes The `model` field uses a protocol prefix format: `[protocol/]model-identifier` @@ -111,7 +114,8 @@ The `model` field uses a protocol prefix format: `[protocol/]model-identifier` | `model_name` | Yes | User-facing alias for the model | | `model` | Yes | Protocol and model identifier (e.g., `openai/gpt-5.4`) | | `api_base` | No | API endpoint URL | -| `api_key` | No* | API authentication key | +| `api_keys` | No | API authentication keys (array; supports multiple keys for load balancing) | +| `enabled` | No | Whether this model entry is active. Defaults to `true` during migration for models with API keys or named `local-model`. Set to `false` to disable. | | `proxy` | No | HTTP proxy URL | | `auth_method` | No | Authentication method: `oauth`, `token` | | `connect_mode` | No | Connection mode for CLI providers: `stdio`, `grpc` | @@ -119,11 +123,13 @@ The `model` field uses a protocol prefix format: `[protocol/]model-identifier` | `max_tokens_field` | No | Field name for max tokens | | `request_timeout` | No | HTTP request timeout in seconds; `<=0` uses default `120s` | -*`api_key` is required for HTTP-based protocols unless `api_base` points to a local server. +> **Note**: `api_key` (singular) has been **removed** in V2 configs. Only `api_keys` (array) is supported. During migration from V0/V1, both `api_key` and `api_keys` are automatically merged into the new `api_keys` array. ## Load Balancing -Configure multiple endpoints for the same model to distribute load: +There are two ways to configure load balancing: + +### Option 1: Multiple API Keys in `api_keys` (Recommended) ```json { @@ -131,19 +137,45 @@ Configure multiple endpoints for the same model to distribute load: { "model_name": "gpt4", "model": "openai/gpt-5.4", - "api_key": "sk-key1", + "api_keys": ["sk-key1", "sk-key2", "sk-key3"], + "api_base": "https://api.openai.com/v1" + } + ] +} +``` + +Or via `.security.yml`: + +```yaml +model_list: + gpt4: + api_keys: + - "sk-key1" + - "sk-key2" + - "sk-key3" +``` + +### Option 2: Multiple Model Entries + +```json +{ + "model_list": [ + { + "model_name": "gpt4", + "model": "openai/gpt-5.4", + "api_keys": ["sk-key1"], "api_base": "https://api1.example.com/v1" }, { "model_name": "gpt4", "model": "openai/gpt-5.4", - "api_key": "sk-key2", + "api_keys": ["sk-key2"], "api_base": "https://api2.example.com/v1" }, { "model_name": "gpt4", "model": "openai/gpt-5.4", - "api_key": "sk-key3", + "api_keys": ["sk-key3"], "api_base": "https://api3.example.com/v1" } ] @@ -162,7 +194,7 @@ With `model_list`, adding a new provider requires zero code changes: { "model_name": "my-custom-llm", "model": "openai/my-model-v1", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "api_base": "https://api.your-provider.com/v1" } ] @@ -173,11 +205,12 @@ Just specify `openai/` as the protocol (or omit it for the default), and provide ## Backward Compatibility -During the migration period, your existing `providers` configuration will continue to work: +During the migration period, your existing V0/V1 config will be auto-migrated to V2: 1. If `model_list` is empty and `providers` has data, the system auto-converts internally -2. A deprecation warning is logged: `"providers config is deprecated, please migrate to model_list"` -3. All existing functionality remains unchanged +2. Both `api_key` (singular) and `api_keys` (array) in V0/V1 configs are merged into the new `api_keys` array +3. A deprecation warning is logged: `"providers config is deprecated, please migrate to model_list"` +4. All existing functionality remains unchanged ## Migration Checklist @@ -212,7 +245,7 @@ unknown protocol "xxx" in model "xxx/model-name" api_key or api_base is required for HTTP-based protocol "xxx" ``` -**Solution**: Provide `api_key` and/or `api_base` for HTTP-based providers. +**Solution**: Provide `api_keys` and/or `api_base` for HTTP-based providers. ## Need Help? diff --git a/docs/my/docker.md b/docs/my/docker.md index 8fe1aed8c..2f9cac3fd 100644 --- a/docs/my/docker.md +++ b/docs/my/docker.md @@ -91,19 +91,19 @@ picoclaw onboard { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key", + "api_keys": ["sk-your-api-key"], "api_base":"https://ark.cn-beijing.volces.com/api/coding/v3" }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "request_timeout": 300 }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "your-anthropic-key" + "api_keys": ["your-anthropic-key"] } ], "tools": { diff --git a/docs/providers.md b/docs/providers.md index c6d442a3b..43cba55b5 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -79,22 +79,22 @@ This design also enables **multi-agent support** with flexible provider selectio { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -117,7 +117,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to { "model_name": "voice-gemini", "model": "gemini/gemini-2.5-flash", - "api_key": "your-gemini-key" + "api_keys": ["your-gemini-key"] } ], "voice": { @@ -140,7 +140,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -150,7 +150,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -160,7 +160,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ``` @@ -170,7 +170,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to { "model_name": "glm-4.7", "model": "openai/glm-4.7", - "api_key": "your-z.ai-key" + "api_keys": ["your-z.ai-key"], "api_base": "https://api.z.ai/api/coding/paas/v4" } ``` @@ -181,7 +181,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -191,7 +191,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] } ``` @@ -205,7 +205,7 @@ For direct Anthropic API access or custom endpoints that only support Anthropic' { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -233,7 +233,7 @@ For direct Anthropic API access or custom endpoints that only support Anthropic' "model_name": "my-custom-model", "model": "openai/custom-model", "api_base": "https://my-proxy.com/v1", - "api_key": "sk-...", + "api_keys": ["sk-..."], "request_timeout": 300 } ``` @@ -245,7 +245,7 @@ For direct Anthropic API access or custom endpoints that only support Anthropic' "model_name": "lite-gpt4", "model": "litellm/lite-gpt4", "api_base": "http://localhost:4000/v1", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -259,7 +259,7 @@ If the standard Zhipu endpoint (`https://open.bigmodel.cn/api/paas/v4`) returns { "model_name": "glm-4.7", "model": "openai/glm-4.7", - "api_key": "your-zhipu-api-key", + "api_keys": ["your-zhipu-api-key"], "api_base": "https://api.z.ai/api/coding/paas/v4" } ``` @@ -277,13 +277,13 @@ Configure multiple endpoints for the same model name—PicoClaw will automatical "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -302,17 +302,17 @@ It also applies cooldown tracking per candidate to avoid immediately retrying a "model_name": "qwen-main", "model": "openai/qwen3.5:cloud", "api_base": "https://api.example.com/v1", - "api_key": "sk-main" + "api_keys": ["sk-main"] }, { "model_name": "deepseek-backup", "model": "deepseek/deepseek-chat", - "api_key": "sk-backup-1" + "api_keys": ["sk-backup-1"] }, { "model_name": "gemini-backup", "model": "gemini/gemini-2.5-flash", - "api_key": "sk-backup-2" + "api_keys": ["sk-backup-2"] } ], "agents": { @@ -330,7 +330,7 @@ If you use key-level failover for the same model, PicoClaw can chain through add #### Migration from Legacy `providers` Config -The old `providers` configuration is **deprecated** but still supported for backward compatibility. +The old `providers` configuration is **deprecated** and has been removed in V2. Existing V0/V1 configs are auto-migrated. **Old Config (deprecated):** @@ -355,11 +355,12 @@ The old `providers` configuration is **deprecated** but still supported for back ```json { + "version": 2, "model_list": [ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ], "agents": { diff --git a/docs/pt-br/configuration.md b/docs/pt-br/configuration.md index cb836ce0f..27cd6d21f 100644 --- a/docs/pt-br/configuration.md +++ b/docs/pt-br/configuration.md @@ -335,15 +335,15 @@ Configure múltiplos endpoints para o mesmo nome de modelo — PicoClaw fará ro ```json { "model_list": [ - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_key": "sk-key1" }, - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_key": "sk-key2" } + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_keys": ["sk-key1"] }, + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_keys": ["sk-key2"] } ] } ``` #### Migração da Configuração Legada `providers` -A configuração antiga `providers` está **depreciada** mas ainda é suportada. Veja [docs/migration/model-list-migration.md](../migration/model-list-migration.md). +A configuração antiga `providers` está **depreciada** e foi removida no V2. Configs V0/V1 existentes são auto-migradas. Veja [docs/migration/model-list-migration.md](../migration/model-list-migration.md). ### Arquitetura de Providers diff --git a/docs/pt-br/docker.md b/docs/pt-br/docker.md index bac48954b..a17dc64ec 100644 --- a/docs/pt-br/docker.md +++ b/docs/pt-br/docker.md @@ -92,19 +92,19 @@ picoclaw onboard { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key", + "api_keys": ["sk-your-api-key"], "api_base":"https://ark.cn-beijing.volces.com/api/coding/v3" }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "request_timeout": 300 }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "your-anthropic-key" + "api_keys": ["your-anthropic-key"] } ], "tools": { diff --git a/docs/pt-br/providers.md b/docs/pt-br/providers.md index 0f7a4b5a1..c7c6305e2 100644 --- a/docs/pt-br/providers.md +++ b/docs/pt-br/providers.md @@ -73,22 +73,22 @@ Este design também permite **suporte multi-agente** com seleção flexível de { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -107,7 +107,7 @@ Este design também permite **suporte multi-agente** com seleção flexível de { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -117,7 +117,7 @@ Este design também permite **suporte multi-agente** com seleção flexível de { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -127,7 +127,7 @@ Este design também permite **suporte multi-agente** com seleção flexível de { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ``` @@ -137,7 +137,7 @@ Este design também permite **suporte multi-agente** com seleção flexível de { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -147,7 +147,7 @@ Este design também permite **suporte multi-agente** com seleção flexível de { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] } ``` @@ -161,7 +161,7 @@ Para acesso direto à API Anthropic ou endpoints personalizados que suportam ape { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -189,7 +189,7 @@ Para acesso direto à API Anthropic ou endpoints personalizados que suportam ape "model_name": "my-custom-model", "model": "openai/custom-model", "api_base": "https://my-proxy.com/v1", - "api_key": "sk-...", + "api_keys": ["sk-..."], "request_timeout": 300 } ``` @@ -201,7 +201,7 @@ Para acesso direto à API Anthropic ou endpoints personalizados que suportam ape "model_name": "lite-gpt4", "model": "litellm/lite-gpt4", "api_base": "http://localhost:4000/v1", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -218,13 +218,13 @@ Configure múltiplos endpoints para o mesmo nome de modelo — o PicoClaw fará "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -232,7 +232,7 @@ Configure múltiplos endpoints para o mesmo nome de modelo — o PicoClaw fará #### Migração da Configuração Legacy `providers` -A configuração antiga `providers` está **descontinuada** mas ainda é suportada para compatibilidade retroativa. +A configuração antiga `providers` está **descontinuada** e foi removida no V2. Configs V0/V1 existentes são auto-migradas. **Configuração Antiga (descontinuada):** @@ -257,11 +257,12 @@ A configuração antiga `providers` está **descontinuada** mas ainda é suporta ```json { + "version": 2, "model_list": [ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ], "agents": { @@ -282,7 +283,7 @@ O PicoClaw roteia provedores por família de protocolo: - Protocolo Anthropic: Comportamento nativo da API Claude. - Caminho Codex/OAuth: Rota de autenticação OAuth/token da OpenAI. -Isso mantém o runtime leve enquanto torna novos backends compatíveis com OpenAI basicamente uma operação de configuração (`api_base` + `api_key`). +Isso mantém o runtime leve enquanto torna novos backends compatíveis com OpenAI basicamente uma operação de configuração (`api_base` + `api_keys`).
Zhipu diff --git a/docs/security_configuration.md b/docs/security_configuration.md index f4fe0e304..311c1790e 100644 --- a/docs/security_configuration.md +++ b/docs/security_configuration.md @@ -401,7 +401,7 @@ The pattern is: `PICOCLAW_
__` with underscores separating p 3. **Set file permissions**: `chmod 600 ~/.picoclaw/.security.yml` 4. **Use different keys** for different environments (dev, staging, production) 5. **Rotate keys regularly** and update `.security.yml` -6. **Backup securely**: Encrypt backups containing `.security.yml` +6. **Backup securely**: Encrypt backups containing `.security.yml`. Note that config migrations automatically create date-stamped backups (e.g., `config.json.20260330.bak` and `.security.yml.20260330.bak`) 7. **Review access**: Ensure only authorized users have read access to the file ## API @@ -444,7 +444,7 @@ Returns the path to `.security.yml` relative to the config file. ```json { - "version": 1, + "version": 2, "agents": { "defaults": { "workspace": "~/picoclaw-workspace", @@ -557,6 +557,8 @@ go test ./pkg/config -run TestSecurityConfig ### Step 1: Backup your config +The system automatically creates a date-stamped backup before saving a migrated config (e.g., `config.json.20260330.bak` and `.security.yml.20260330.bak`). If you prefer a manual backup: + ```bash cp ~/.picoclaw/config.json ~/.picoclaw/config.json.backup ``` @@ -597,9 +599,11 @@ Test your models and channels to ensure everything works correctly. ### Step 8: Clean up (optional) -If everything works, you can delete the backup: +If everything works, you can delete the backups: ```bash rm ~/.picoclaw/config.json.backup +# Also remove auto-generated date-stamped backups if desired: +rm ~/.picoclaw/config.json.20*.bak ~/.picoclaw/.security.yml.20*.bak ``` ## Advanced: Encrypted API Keys diff --git a/docs/vi/configuration.md b/docs/vi/configuration.md index b75215c3f..56eb8f557 100644 --- a/docs/vi/configuration.md +++ b/docs/vi/configuration.md @@ -335,15 +335,15 @@ Cấu hình nhiều endpoint cho cùng tên mô hình — PicoClaw sẽ tự đ ```json { "model_list": [ - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_key": "sk-key1" }, - { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_key": "sk-key2" } + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", "api_keys": ["sk-key1"] }, + { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_keys": ["sk-key2"] } ] } ``` #### Di Chuyển Từ Cấu Hình `providers` Cũ -Cấu hình `providers` cũ đã **bị deprecated** nhưng vẫn được hỗ trợ. Xem [docs/migration/model-list-migration.md](../migration/model-list-migration.md). +Cấu hình `providers` cũ đã **bị deprecated** và đã được loại bỏ trong V2. Các cấu hình V0/V1 hiện có sẽ được tự động migrate. Xem [docs/migration/model-list-migration.md](../migration/model-list-migration.md). ### Kiến Trúc Provider diff --git a/docs/vi/docker.md b/docs/vi/docker.md index eddc20a75..e6bc74b1a 100644 --- a/docs/vi/docker.md +++ b/docs/vi/docker.md @@ -92,19 +92,19 @@ picoclaw onboard { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key", + "api_keys": ["sk-your-api-key"], "api_base":"https://ark.cn-beijing.volces.com/api/coding/v3" }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "request_timeout": 300 }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "your-anthropic-key" + "api_keys": ["your-anthropic-key"] } ], "tools": { diff --git a/docs/vi/providers.md b/docs/vi/providers.md index 09b51c56b..ffd992645 100644 --- a/docs/vi/providers.md +++ b/docs/vi/providers.md @@ -73,22 +73,22 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -107,7 +107,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -117,7 +117,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -127,7 +127,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ``` @@ -137,7 +137,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -147,7 +147,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] } ``` @@ -161,7 +161,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -189,7 +189,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr "model_name": "my-custom-model", "model": "openai/custom-model", "api_base": "https://my-proxy.com/v1", - "api_key": "sk-...", + "api_keys": ["sk-..."], "request_timeout": 300 } ``` @@ -201,7 +201,7 @@ Thiết kế này cũng cho phép **hỗ trợ đa agent** với lựa chọn pr "model_name": "lite-gpt4", "model": "litellm/lite-gpt4", "api_base": "http://localhost:4000/v1", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -218,13 +218,13 @@ Cấu hình nhiều endpoint cho cùng tên mô hình — PicoClaw sẽ tự đ "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -232,7 +232,7 @@ Cấu hình nhiều endpoint cho cùng tên mô hình — PicoClaw sẽ tự đ #### Di Chuyển Từ Cấu Hình Legacy `providers` -Cấu hình `providers` cũ đã **ngừng hỗ trợ** nhưng vẫn được hỗ trợ để tương thích ngược. +Cấu hình `providers` cũ đã **bị deprecated** và đã được loại bỏ trong V2. Các cấu hình V0/V1 hiện có sẽ được tự động migrate. **Cấu hình cũ (ngừng hỗ trợ):** @@ -257,11 +257,12 @@ Cấu hình `providers` cũ đã **ngừng hỗ trợ** nhưng vẫn được h ```json { + "version": 2, "model_list": [ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ], "agents": { @@ -282,7 +283,7 @@ PicoClaw định tuyến provider theo họ giao thức: - Giao thức Anthropic: Hành vi API native của Claude. - Đường dẫn Codex/OAuth: Tuyến xác thực OAuth/token của OpenAI. -Điều này giữ runtime nhẹ trong khi làm cho backend tương thích OpenAI mới chủ yếu là thao tác cấu hình (`api_base` + `api_key`). +Điều này giữ runtime nhẹ trong khi làm cho backend tương thích OpenAI mới chủ yếu là thao tác cấu hình (`api_base` + `api_keys`).
Zhipu diff --git a/docs/zh/configuration.md b/docs/zh/configuration.md index c27a439f0..d4366b6a8 100644 --- a/docs/zh/configuration.md +++ b/docs/zh/configuration.md @@ -386,22 +386,22 @@ Agent 读取 HEARTBEAT.md { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -421,7 +421,7 @@ Agent 读取 HEARTBEAT.md { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -434,7 +434,7 @@ Agent 读取 HEARTBEAT.md { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -447,7 +447,7 @@ Agent 读取 HEARTBEAT.md { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ``` @@ -460,7 +460,7 @@ Agent 读取 HEARTBEAT.md { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -473,7 +473,7 @@ Agent 读取 HEARTBEAT.md { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] } ``` @@ -485,7 +485,7 @@ Agent 读取 HEARTBEAT.md { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -514,7 +514,7 @@ Agent 读取 HEARTBEAT.md "model_name": "my-custom-model", "model": "openai/custom-model", "api_base": "https://my-proxy.com/v1", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -533,13 +533,13 @@ PicoClaw 只剥离最外层的 `litellm/` 前缀再发送请求,因此 `litell "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -547,7 +547,7 @@ PicoClaw 只剥离最外层的 `litellm/` 前缀再发送请求,因此 `litell #### 从旧版 `providers` 配置迁移 -旧版 `providers` 配置**已废弃**,但仍向后兼容。完整迁移指南见 [docs/migration/model-list-migration.md](../migration/model-list-migration.md)。 +旧版 `providers` 配置**已废弃**,V2 中已移除。现有 V0/V1 配置会自动迁移。完整迁移指南见 [docs/migration/model-list-migration.md](../migration/model-list-migration.md)。 ### Provider 架构 diff --git a/docs/zh/docker.md b/docs/zh/docker.md index 8aed1e86b..f840290a7 100644 --- a/docs/zh/docker.md +++ b/docs/zh/docker.md @@ -94,19 +94,19 @@ picoclaw onboard { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key", + "api_keys": ["sk-your-api-key"], "api_base":"https://ark.cn-beijing.volces.com/api/coding/v3" }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "your-api-key", + "api_keys": ["your-api-key"], "request_timeout": 300 }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "your-anthropic-key" + "api_keys": ["your-anthropic-key"] } ], "tools": { diff --git a/docs/zh/providers.md b/docs/zh/providers.md index 4bcf7087e..d19dcdd89 100644 --- a/docs/zh/providers.md +++ b/docs/zh/providers.md @@ -75,22 +75,22 @@ { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-your-api-key" + "api_keys": ["sk-your-api-key"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-your-openai-key" + "api_keys": ["sk-your-openai-key"] }, { "model_name": "claude-sonnet-4.6", "model": "anthropic/claude-sonnet-4.6", - "api_key": "sk-ant-your-key" + "api_keys": ["sk-ant-your-key"] }, { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-zhipu-key" + "api_keys": ["your-zhipu-key"] } ], "agents": { @@ -113,7 +113,7 @@ { "model_name": "voice-gemini", "model": "gemini/gemini-2.5-flash", - "api_key": "your-gemini-key" + "api_keys": ["your-gemini-key"] } ], "voice": { @@ -136,7 +136,7 @@ { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -146,7 +146,7 @@ { "model_name": "ark-code-latest", "model": "volcengine/ark-code-latest", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -156,7 +156,7 @@ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ``` @@ -166,7 +166,7 @@ { "model_name": "deepseek-chat", "model": "deepseek/deepseek-chat", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -190,7 +190,7 @@ { "model_name": "claude-opus-4-6", "model": "anthropic-messages/claude-opus-4-6", - "api_key": "sk-ant-your-key", + "api_keys": ["sk-ant-your-key"], "api_base": "https://api.anthropic.com" } ``` @@ -218,7 +218,7 @@ "model_name": "my-custom-model", "model": "openai/custom-model", "api_base": "https://my-proxy.com/v1", - "api_key": "sk-...", + "api_keys": ["sk-..."], "request_timeout": 300 } ``` @@ -230,7 +230,7 @@ "model_name": "lite-gpt4", "model": "litellm/lite-gpt4", "api_base": "http://localhost:4000/v1", - "api_key": "sk-..." + "api_keys": ["sk-..."] } ``` @@ -247,13 +247,13 @@ PicoClaw 在发送请求前仅去除外层 `litellm/` 前缀,因此 `litellm/l "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api1.example.com/v1", - "api_key": "sk-key1" + "api_keys": ["sk-key1"] }, { "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", - "api_key": "sk-key2" + "api_keys": ["sk-key2"] } ] } @@ -272,17 +272,17 @@ PicoClaw 在发送请求前仅去除外层 `litellm/` 前缀,因此 `litellm/l "model_name": "qwen-main", "model": "openai/qwen3.5:cloud", "api_base": "https://api.example.com/v1", - "api_key": "sk-main" + "api_keys": ["sk-main"] }, { "model_name": "deepseek-backup", "model": "deepseek/deepseek-chat", - "api_key": "sk-backup-1" + "api_keys": ["sk-backup-1"] }, { "model_name": "gemini-backup", "model": "gemini/gemini-2.5-flash", - "api_key": "sk-backup-2" + "api_keys": ["sk-backup-2"] } ], "agents": { @@ -300,7 +300,7 @@ PicoClaw 在发送请求前仅去除外层 `litellm/` 前缀,因此 `litellm/l #### 从旧的 `providers` 配置迁移 -旧的 `providers` 配置格式**已弃用**,但为向后兼容仍支持。 +旧的 `providers` 配置格式**已弃用**,V2 中已移除。现有 V0/V1 配置会自动迁移。 **旧配置(已弃用):** @@ -325,11 +325,12 @@ PicoClaw 在发送请求前仅去除外层 `litellm/` 前缀,因此 `litellm/l ```json { + "version": 2, "model_list": [ { "model_name": "glm-4.7", "model": "zhipu/glm-4.7", - "api_key": "your-key" + "api_keys": ["your-key"] } ], "agents": { diff --git a/pkg/gateway/gateway.go b/pkg/gateway/gateway.go index b66ec68b4..a47bf2ac6 100644 --- a/pkg/gateway/gateway.go +++ b/pkg/gateway/gateway.go @@ -107,11 +107,13 @@ func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) error logger.Fatalf("config pre-check failed: %v", err) } - logger.SetLevelFromString(cfg.Gateway.LogLevel) - + // Debug mode permanently overrides the config log level to DEBUG. if debug { logger.SetLevel(logger.DEBUG) fmt.Println("🔍 Debug mode enabled") + } else { + logger.SetLevelFromString(cfg.Gateway.LogLevel) + logger.Infof("Log level set to %q", cfg.Gateway.LogLevel) } // Enforce singleton: write PID file with generated token. @@ -201,7 +203,7 @@ func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) error logger.Warn("Config reload skipped: another reload is in progress") continue } - err := executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup) + err := executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup, debug) if err != nil { logger.Errorf("Config reload failed: %v", err) } @@ -218,7 +220,7 @@ func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) error runningServices.reloading.Store(false) continue } - err = executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup) + err = executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup, debug) if err != nil { logger.Errorf("Manual reload failed: %v", err) } else { @@ -243,12 +245,13 @@ func executeReload( runningServices *services, msgBus *bus.MessageBus, allowEmptyStartup bool, + debug bool, ) error { defer runningServices.reloading.Store(false) overridePicoToken(newCfg, runningServices.authToken) - return handleConfigReload(ctx, agentLoop, newCfg, provider, runningServices, msgBus, allowEmptyStartup) + return handleConfigReload(ctx, agentLoop, newCfg, provider, runningServices, msgBus, allowEmptyStartup, debug) } func createStartupProvider( @@ -420,6 +423,7 @@ func handleConfigReload( runningServices *services, msgBus *bus.MessageBus, allowEmptyStartup bool, + debug bool, ) error { logger.Info("🔄 Config file changed, reloading...") @@ -468,6 +472,14 @@ func handleConfigReload( } logger.Info(" ✓ Provider, configuration, and services reloaded successfully (thread-safe)") + + // Debug mode permanently overrides the config log level to DEBUG. + if !debug { + // Update log level last so that reload-related info/warn logs above are not suppressed. + logger.SetLevelFromString(newCfg.Gateway.LogLevel) + logger.Infof("Log level changing from current to %q", newCfg.Gateway.LogLevel) + } + return nil }