mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
Merge branch 'main' into t3
This commit is contained in:
+80
-81
@@ -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
|
||||
|
||||
|
||||
+14
-11
@@ -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`).
|
||||
|
||||
<details>
|
||||
<summary><b>Zhipu (legacy providers format)</b></summary>
|
||||
|
||||
@@ -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://<base64>` 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 |
|
||||
|
||||
+3
-3
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-3
@@ -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": {
|
||||
|
||||
+17
-16
@@ -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": {
|
||||
|
||||
@@ -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 アーキテクチャ
|
||||
|
||||
|
||||
+3
-3
@@ -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": {
|
||||
|
||||
+29
-17
@@ -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`)のみで実現しています。
|
||||
|
||||
<details>
|
||||
<summary><b>Zhipu 設定例</b></summary>
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
+3
-3
@@ -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": {
|
||||
|
||||
+23
-22
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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": {
|
||||
|
||||
+18
-17
@@ -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`).
|
||||
|
||||
<details>
|
||||
<summary><b>Zhipu</b></summary>
|
||||
|
||||
@@ -401,7 +401,7 @@ The pattern is: `PICOCLAW_<SECTION>_<KEY>_<FIELD>` 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-3
@@ -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": {
|
||||
|
||||
+18
-17
@@ -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`).
|
||||
|
||||
<details>
|
||||
<summary><b>Zhipu</b></summary>
|
||||
|
||||
+14
-14
@@ -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 架构
|
||||
|
||||
|
||||
+3
-3
@@ -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": {
|
||||
|
||||
+20
-19
@@ -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": {
|
||||
|
||||
+17
-5
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user