Merge branch 'main' into t3
This commit is contained in:
commit
f9bfa6b9a8
24 changed files with 340 additions and 270 deletions
|
|
@ -11,24 +11,35 @@ PicoClaw uses a schema versioning system for `config.json` to ensure smooth upgr
|
||||||
- **Changes**: Added `version` field to Config struct
|
- **Changes**: Added `version` field to Config struct
|
||||||
- **Migration**: No structural changes needed for existing configs
|
- **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
|
## How It Works
|
||||||
|
|
||||||
### Automatic Migration
|
### Automatic Migration
|
||||||
When you load a config file:
|
When you load a config file:
|
||||||
1. The system first reads the `version` field from the JSON
|
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
|
3. If the loaded version is less than the latest, migrations are applied incrementally
|
||||||
4. The version number is updated automatically
|
4. Before saving, the system automatically creates a date-stamped backup of `config.json` and `.security.yml`
|
||||||
5. The migrated config is automatically saved back to disk
|
5. The version number is updated automatically
|
||||||
|
6. The migrated config is automatically saved back to disk
|
||||||
|
|
||||||
### Version Field
|
### Version Field
|
||||||
The `version` field in `config.json` indicates the schema version:
|
The `version` field in `config.json` indicates the schema version:
|
||||||
- `0` or missing: Legacy config (no version field)
|
- `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
|
```json
|
||||||
{
|
{
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"agents": {...},
|
"agents": {...},
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
@ -54,25 +65,25 @@ type ConfigV2 struct {
|
||||||
### Step 2: Update Current Config Version
|
### Step 2: Update Current Config Version
|
||||||
|
|
||||||
```go
|
```go
|
||||||
const CurrentConfigVersion = 2 // Increment this
|
const CurrentVersion = 2 // Increment this
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: Add a Loader Function
|
### Step 3: Add a Loader Function
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// loadConfigV2 loads a version 2 config
|
// loadConfigV3 loads a version 3 config
|
||||||
func loadConfigV2(data []byte) (*Config, error) {
|
func loadConfigV3(data []byte) (*Config, error) {
|
||||||
cfg := DefaultConfig()
|
cfg := DefaultConfig()
|
||||||
|
|
||||||
// Parse to ConfigV2 struct
|
// Parse to ConfigV3 struct
|
||||||
var v2 ConfigV2
|
var v3 ConfigV3
|
||||||
if err := json.Unmarshal(data, &v2); err != nil {
|
if err := json.Unmarshal(data, &v3); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to current Config
|
// Convert to current Config
|
||||||
cfg.Version = v2.Version
|
cfg.Version = v3.Version
|
||||||
cfg.Agents = v2.Agents
|
cfg.Agents = v3.Agents
|
||||||
// ... map other fields
|
// ... map other fields
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
|
|
@ -82,29 +93,12 @@ func loadConfigV2(data []byte) (*Config, error) {
|
||||||
### Step 4: Add Migration Logic
|
### Step 4: Add Migration Logic
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// applyMigration applies a single migration step from fromVersion to toVersion
|
func (c *configV2) Migrate() (*Config, error) {
|
||||||
func applyMigration(cfg *Config, fromVersion, toVersion int) (*Config, error) {
|
// Apply V2→V3 structural changes here
|
||||||
switch toVersion {
|
migrated := &c.Config
|
||||||
case 1:
|
migrated.Version = 3
|
||||||
// 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
|
// Apply structural changes
|
||||||
if cfg.SomeOldField != "" {
|
return migrated, nil
|
||||||
migrated.SomeNewField = cfg.SomeOldField
|
|
||||||
}
|
|
||||||
return &migrated, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported migration target version: %d", toVersion)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -120,7 +114,9 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
case 1:
|
case 1:
|
||||||
cfg, err = loadConfigV1(data)
|
cfg, err = loadConfigV1(data)
|
||||||
case 2:
|
case 2:
|
||||||
cfg, err = loadConfigV2(data)
|
cfg, err = loadConfig(data)
|
||||||
|
case 3:
|
||||||
|
cfg, err = loadConfigV3(data)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported config version: %d", versionInfo.Version)
|
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`:
|
Create a test in `config_migration_test.go`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func TestMigrateV1ToV2(t *testing.T) {
|
func TestMigrateV2ToV3(t *testing.T) {
|
||||||
// Create a version 1 config
|
// Create a version 2 config
|
||||||
v1Config := Config{
|
v2Config := Config{
|
||||||
Version: 1,
|
Version: 2,
|
||||||
// ... set up test data
|
// ... set up test data
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply migration
|
// Apply migration
|
||||||
migrated, err := applyMigration(&v1Config, 1, 2)
|
migrated, err := v2Config.Migrate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Migration failed: %v", err)
|
t.Fatalf("Migration failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify version is updated
|
// Verify version is updated
|
||||||
if migrated.Version != 2 {
|
if migrated.Version != 3 {
|
||||||
t.Errorf("Expected version 2, got %d", migrated.Version)
|
t.Errorf("Expected version 3, got %d", migrated.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify data is preserved/transformed correctly
|
// 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
|
3. **No Data Loss**: Migrations should preserve all user settings
|
||||||
4. **Idempotent**: Running the same migration multiple times should be safe
|
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
|
5. **Auto-Save**: Migrated configs are automatically saved to update the user's file
|
||||||
6. **Test Thoroughly**: Test with real user config files
|
6. **Auto-Backup**: Before saving, the system creates a date-stamped backup of `config.json` and `.security.yml`
|
||||||
7. **Update Defaults**: Keep `defaults.go` in sync with the latest schema
|
7. **Test Thoroughly**: Test with real user config files
|
||||||
|
8. **Update Defaults**: Keep `defaults.go` in sync with the latest schema
|
||||||
|
|
||||||
## Example Migration
|
## Example Migration
|
||||||
|
|
||||||
### Scenario: Adding a new field with default value
|
### Scenario: Adding a new field with default value
|
||||||
|
|
||||||
Old config (version 1):
|
Old config (version 2):
|
||||||
```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):
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"agents": {
|
"model_list": [
|
||||||
"defaults": {
|
{
|
||||||
"max_tokens": 32768,
|
"model_name": "gpt-5.4",
|
||||||
"new_feature_enabled": false
|
"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
|
## Troubleshooting
|
||||||
|
|
||||||
### Config Not Upgrading
|
### Config Not Upgrading
|
||||||
- Check that `CurrentConfigVersion` is incremented
|
- Check that `CurrentVersion` is incremented
|
||||||
- Verify migration logic in `applyMigration()` handles the target version
|
- Verify migration logic handles the target version
|
||||||
- Ensure `migrateConfig()` is called in `LoadConfig()`
|
- Ensure `Migrate()` is called in `LoadConfig()`
|
||||||
|
|
||||||
### Migration Errors
|
### Migration Errors
|
||||||
- Check error messages for specific migration failures
|
- Check error messages for specific migration failures
|
||||||
|
|
@ -227,4 +225,5 @@ New config (version 2):
|
||||||
- Ensure all fields are copied during migration
|
- Ensure all fields are copied during migration
|
||||||
- Check that the migration doesn't overwrite values with defaults unnecessarily
|
- Check that the migration doesn't overwrite values with defaults unnecessarily
|
||||||
- Review the conversion logic in the loader functions
|
- Review the conversion logic in the loader functions
|
||||||
|
- Check the auto-backup files (e.g., `config.json.20260330.bak`) to recover original data
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
- **Different agents, different providers**: Each agent can use its own LLM provider
|
||||||
- **Model fallbacks**: Configure primary and fallback models for resilience
|
- **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
|
- **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)
|
#### 🔒 Security Configuration (Recommended)
|
||||||
|
|
||||||
|
|
@ -581,22 +582,22 @@ For complete documentation, see [`security_configuration.md`](security_configura
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"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
|
#### 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_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"api_base": "https://api.anthropic.com"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -759,13 +762,13 @@ model_list:
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"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
|
#### 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
|
### Provider Architecture
|
||||||
|
|
||||||
|
|
@ -783,7 +786,7 @@ PicoClaw routes providers by protocol family:
|
||||||
- **Anthropic**: Claude-native API behavior.
|
- **Anthropic**: Claude-native API behavior.
|
||||||
- **Codex/OAuth**: OpenAI OAuth/token authentication route.
|
- **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>
|
<details>
|
||||||
<summary><b>Zhipu (legacy providers format)</b></summary>
|
<summary><b>Zhipu (legacy providers format)</b></summary>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Credential Encryption
|
# 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.
|
Encrypted keys are stored as `enc://<base64>` strings and decrypted automatically at startup.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -42,6 +42,8 @@ enc://AAAA...base64...
|
||||||
|
|
||||||
## Supported `api_key` Formats
|
## 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 |
|
| Format | Example | Behaviour |
|
||||||
|--------|---------|-----------|
|
|--------|---------|-----------|
|
||||||
| Plaintext | `sk-abc123` | Used as-is |
|
| Plaintext | `sk-abc123` | Used as-is |
|
||||||
|
|
|
||||||
|
|
@ -95,19 +95,19 @@ picoclaw onboard
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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"
|
"api_base":"https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/claude-sonnet-4.6",
|
"model": "anthropic/claude-sonnet-4.6",
|
||||||
"api_key": "your-anthropic-key"
|
"api_keys": ["your-anthropic-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -334,15 +334,15 @@ Configurez plusieurs endpoints pour le même nom de modèle — PicoClaw effectu
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_list": [
|
"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://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_key": "sk-key2" }
|
{ "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`
|
#### 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
|
### Architecture des Providers
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,19 +92,19 @@ picoclaw onboard
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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"
|
"api_base":"https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/claude-sonnet-4.6",
|
"model": "anthropic/claude-sonnet-4.6",
|
||||||
"api_key": "your-anthropic-key"
|
"api_keys": ["your-anthropic-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -73,22 +73,22 @@ Cette conception permet également le **support multi-agents** avec une sélecti
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -107,7 +107,7 @@ Cette conception permet également le **support multi-agents** avec une sélecti
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "glm-4.7",
|
||||||
"model": "zhipu/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_name": "deepseek-chat",
|
||||||
"model": "deepseek/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"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_name": "my-custom-model",
|
||||||
"model": "openai/custom-model",
|
"model": "openai/custom-model",
|
||||||
"api_base": "https://my-proxy.com/v1",
|
"api_base": "https://my-proxy.com/v1",
|
||||||
"api_key": "sk-...",
|
"api_keys": ["sk-..."],
|
||||||
"request_timeout": 300
|
"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_name": "lite-gpt4",
|
||||||
"model": "litellm/lite-gpt4",
|
"model": "litellm/lite-gpt4",
|
||||||
"api_base": "http://localhost:4000/v1",
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"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`
|
#### 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) :**
|
**Ancienne configuration (dépréciée) :**
|
||||||
|
|
||||||
|
|
@ -257,11 +257,12 @@ L'ancienne configuration `providers` est **dépréciée** mais toujours prise en
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"version": 2,
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-key"
|
"api_keys": ["your-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
|
||||||
|
|
@ -335,15 +335,15 @@ HEARTBEAT_OK を返信 ユーザーが直接結果を受信
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_list": [
|
"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://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_key": "sk-key2" }
|
{ "model_name": "gpt-5.4", "model": "openai/gpt-5.4", "api_base": "https://api2.example.com/v1", "api_keys": ["sk-key2"] }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 旧 `providers` 設定からの移行
|
#### 旧 `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 アーキテクチャ
|
### Provider アーキテクチャ
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,19 +94,19 @@ picoclaw onboard
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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"
|
"api_base":"https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/claude-sonnet-4.6",
|
"model": "anthropic/claude-sonnet-4.6",
|
||||||
"api_key": "your-anthropic-key"
|
"api_keys": ["your-anthropic-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -73,22 +73,22 @@
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "sk-..."
|
"api_keys": ["sk-..."]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/ark-code-latest",
|
"model": "volcengine/ark-code-latest",
|
||||||
"api_key": "sk-..."
|
"api_keys": ["sk-..."]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -127,7 +127,18 @@
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/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_name": "deepseek-chat",
|
||||||
"model": "deepseek/deepseek-chat",
|
"model": "deepseek/deepseek-chat",
|
||||||
"api_key": "sk-..."
|
"api_keys": ["sk-..."]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -147,7 +158,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"api_base": "https://api.anthropic.com"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -189,7 +200,7 @@ Anthropic API への直接アクセスや、Anthropic のネイティブメッ
|
||||||
"model_name": "my-custom-model",
|
"model_name": "my-custom-model",
|
||||||
"model": "openai/custom-model",
|
"model": "openai/custom-model",
|
||||||
"api_base": "https://my-proxy.com/v1",
|
"api_base": "https://my-proxy.com/v1",
|
||||||
"api_key": "sk-...",
|
"api_keys": ["sk-..."],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -201,7 +212,7 @@ Anthropic API への直接アクセスや、Anthropic のネイティブメッ
|
||||||
"model_name": "lite-gpt4",
|
"model_name": "lite-gpt4",
|
||||||
"model": "litellm/lite-gpt4",
|
"model": "litellm/lite-gpt4",
|
||||||
"api_base": "http://localhost:4000/v1",
|
"api_base": "http://localhost:4000/v1",
|
||||||
"api_key": "sk-..."
|
"api_keys": ["sk-..."]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -218,13 +229,13 @@ PicoClaw はリクエスト送信前に外側の `litellm/` プレフィック
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_keys": ["sk-key2"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -232,7 +243,7 @@ PicoClaw はリクエスト送信前に外側の `litellm/` プレフィック
|
||||||
|
|
||||||
#### レガシー `providers` 設定からの移行
|
#### レガシー `providers` 設定からの移行
|
||||||
|
|
||||||
旧 `providers` 設定形式は**非推奨**ですが、後方互換性のためまだサポートされています。
|
旧 `providers` 設定形式は**非推奨**となり、V2 で削除されました。既存の V0/V1 設定は自動的に移行されます。
|
||||||
|
|
||||||
**旧設定(非推奨):**
|
**旧設定(非推奨):**
|
||||||
|
|
||||||
|
|
@ -257,11 +268,12 @@ PicoClaw はリクエスト送信前に外側の `litellm/` プレフィック
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"version": 2,
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-key"
|
"api_keys": ["your-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -282,7 +294,7 @@ PicoClaw はプロトコルファミリーごとに Provider をルーティン
|
||||||
- Anthropic プロトコル:Claude ネイティブ API 動作。
|
- Anthropic プロトコル:Claude ネイティブ API 動作。
|
||||||
- Codex/OAuth パス:OpenAI OAuth/Token 認証ルート。
|
- Codex/OAuth パス:OpenAI OAuth/Token 認証ルート。
|
||||||
|
|
||||||
これによりランタイムを軽量に保ちつつ、新しい OpenAI 互換バックエンドの追加をほぼ設定操作(`api_base` + `api_key`)のみで実現しています。
|
これによりランタイムを軽量に保ちつつ、新しい OpenAI 互換バックエンドの追加をほぼ設定操作(`api_base` + `api_keys`)のみで実現しています。
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Zhipu 設定例</b></summary>
|
<summary><b>Zhipu 設定例</b></summary>
|
||||||
|
|
|
||||||
|
|
@ -50,22 +50,23 @@ The new `model_list` configuration offers several advantages:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"version": 2,
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "sk-your-openai-key",
|
"api_keys": ["sk-your-openai-key"],
|
||||||
"api_base": "https://api.openai.com/v1"
|
"api_base": "https://api.openai.com/v1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "deepseek",
|
||||||
"model": "deepseek/deepseek-chat",
|
"model": "deepseek/deepseek-chat",
|
||||||
"api_key": "sk-your-deepseek-key"
|
"api_keys": ["sk-your-deepseek-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"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
|
## Protocol Prefixes
|
||||||
|
|
||||||
The `model` field uses a protocol prefix format: `[protocol/]model-identifier`
|
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_name` | Yes | User-facing alias for the model |
|
||||||
| `model` | Yes | Protocol and model identifier (e.g., `openai/gpt-5.4`) |
|
| `model` | Yes | Protocol and model identifier (e.g., `openai/gpt-5.4`) |
|
||||||
| `api_base` | No | API endpoint URL |
|
| `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 |
|
| `proxy` | No | HTTP proxy URL |
|
||||||
| `auth_method` | No | Authentication method: `oauth`, `token` |
|
| `auth_method` | No | Authentication method: `oauth`, `token` |
|
||||||
| `connect_mode` | No | Connection mode for CLI providers: `stdio`, `grpc` |
|
| `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 |
|
| `max_tokens_field` | No | Field name for max tokens |
|
||||||
| `request_timeout` | No | HTTP request timeout in seconds; `<=0` uses default `120s` |
|
| `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
|
## 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
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -131,19 +137,45 @@ Configure multiple endpoints for the same model to distribute load:
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-5.4",
|
"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"
|
"api_base": "https://api1.example.com/v1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "sk-key2",
|
"api_keys": ["sk-key2"],
|
||||||
"api_base": "https://api2.example.com/v1"
|
"api_base": "https://api2.example.com/v1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt4",
|
"model_name": "gpt4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "sk-key3",
|
"api_keys": ["sk-key3"],
|
||||||
"api_base": "https://api3.example.com/v1"
|
"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_name": "my-custom-llm",
|
||||||
"model": "openai/my-model-v1",
|
"model": "openai/my-model-v1",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"api_base": "https://api.your-provider.com/v1"
|
"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
|
## 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
|
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"`
|
2. Both `api_key` (singular) and `api_keys` (array) in V0/V1 configs are merged into the new `api_keys` array
|
||||||
3. All existing functionality remains unchanged
|
3. A deprecation warning is logged: `"providers config is deprecated, please migrate to model_list"`
|
||||||
|
4. All existing functionality remains unchanged
|
||||||
|
|
||||||
## Migration Checklist
|
## 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"
|
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?
|
## Need Help?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,19 +91,19 @@ picoclaw onboard
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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"
|
"api_base":"https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/claude-sonnet-4.6",
|
"model": "anthropic/claude-sonnet-4.6",
|
||||||
"api_key": "your-anthropic-key"
|
"api_keys": ["your-anthropic-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -79,22 +79,22 @@ This design also enables **multi-agent support** with flexible provider selectio
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -117,7 +117,7 @@ If `voice.model_name` is not configured, PicoClaw will continue to fall back to
|
||||||
{
|
{
|
||||||
"model_name": "voice-gemini",
|
"model_name": "voice-gemini",
|
||||||
"model": "gemini/gemini-2.5-flash",
|
"model": "gemini/gemini-2.5-flash",
|
||||||
"api_key": "your-gemini-key"
|
"api_keys": ["your-gemini-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"voice": {
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "glm-4.7",
|
||||||
"model": "zhipu/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_name": "glm-4.7",
|
||||||
"model": "openai/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"
|
"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_name": "deepseek-chat",
|
||||||
"model": "deepseek/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"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_name": "my-custom-model",
|
||||||
"model": "openai/custom-model",
|
"model": "openai/custom-model",
|
||||||
"api_base": "https://my-proxy.com/v1",
|
"api_base": "https://my-proxy.com/v1",
|
||||||
"api_key": "sk-...",
|
"api_keys": ["sk-..."],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -245,7 +245,7 @@ For direct Anthropic API access or custom endpoints that only support Anthropic'
|
||||||
"model_name": "lite-gpt4",
|
"model_name": "lite-gpt4",
|
||||||
"model": "litellm/lite-gpt4",
|
"model": "litellm/lite-gpt4",
|
||||||
"api_base": "http://localhost:4000/v1",
|
"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_name": "glm-4.7",
|
||||||
"model": "openai/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"
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"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_name": "qwen-main",
|
||||||
"model": "openai/qwen3.5:cloud",
|
"model": "openai/qwen3.5:cloud",
|
||||||
"api_base": "https://api.example.com/v1",
|
"api_base": "https://api.example.com/v1",
|
||||||
"api_key": "sk-main"
|
"api_keys": ["sk-main"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "deepseek-backup",
|
"model_name": "deepseek-backup",
|
||||||
"model": "deepseek/deepseek-chat",
|
"model": "deepseek/deepseek-chat",
|
||||||
"api_key": "sk-backup-1"
|
"api_keys": ["sk-backup-1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gemini-backup",
|
"model_name": "gemini-backup",
|
||||||
"model": "gemini/gemini-2.5-flash",
|
"model": "gemini/gemini-2.5-flash",
|
||||||
"api_key": "sk-backup-2"
|
"api_keys": ["sk-backup-2"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"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
|
#### 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):**
|
**Old Config (deprecated):**
|
||||||
|
|
||||||
|
|
@ -355,11 +355,12 @@ The old `providers` configuration is **deprecated** but still supported for back
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"version": 2,
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-key"
|
"api_keys": ["your-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
|
||||||
|
|
@ -335,15 +335,15 @@ Configure múltiplos endpoints para o mesmo nome de modelo — PicoClaw fará ro
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"model_list": [
|
"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://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_key": "sk-key2" }
|
{ "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`
|
#### 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
|
### Arquitetura de Providers
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,19 +92,19 @@ picoclaw onboard
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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"
|
"api_base":"https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/claude-sonnet-4.6",
|
"model": "anthropic/claude-sonnet-4.6",
|
||||||
"api_key": "your-anthropic-key"
|
"api_keys": ["your-anthropic-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -73,22 +73,22 @@ Este design também permite **suporte multi-agente** com seleção flexível de
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "glm-4.7",
|
||||||
"model": "zhipu/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_name": "deepseek-chat",
|
||||||
"model": "deepseek/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"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_name": "my-custom-model",
|
||||||
"model": "openai/custom-model",
|
"model": "openai/custom-model",
|
||||||
"api_base": "https://my-proxy.com/v1",
|
"api_base": "https://my-proxy.com/v1",
|
||||||
"api_key": "sk-...",
|
"api_keys": ["sk-..."],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -201,7 +201,7 @@ Para acesso direto à API Anthropic ou endpoints personalizados que suportam ape
|
||||||
"model_name": "lite-gpt4",
|
"model_name": "lite-gpt4",
|
||||||
"model": "litellm/lite-gpt4",
|
"model": "litellm/lite-gpt4",
|
||||||
"api_base": "http://localhost:4000/v1",
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"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`
|
#### 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):**
|
**Configuração Antiga (descontinuada):**
|
||||||
|
|
||||||
|
|
@ -257,11 +257,12 @@ A configuração antiga `providers` está **descontinuada** mas ainda é suporta
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"version": 2,
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-key"
|
"api_keys": ["your-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -282,7 +283,7 @@ O PicoClaw roteia provedores por família de protocolo:
|
||||||
- Protocolo Anthropic: Comportamento nativo da API Claude.
|
- Protocolo Anthropic: Comportamento nativo da API Claude.
|
||||||
- Caminho Codex/OAuth: Rota de autenticação OAuth/token da OpenAI.
|
- 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>
|
<details>
|
||||||
<summary><b>Zhipu</b></summary>
|
<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`
|
3. **Set file permissions**: `chmod 600 ~/.picoclaw/.security.yml`
|
||||||
4. **Use different keys** for different environments (dev, staging, production)
|
4. **Use different keys** for different environments (dev, staging, production)
|
||||||
5. **Rotate keys regularly** and update `.security.yml`
|
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
|
7. **Review access**: Ensure only authorized users have read access to the file
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
@ -444,7 +444,7 @@ Returns the path to `.security.yml` relative to the config file.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"agents": {
|
"agents": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"workspace": "~/picoclaw-workspace",
|
"workspace": "~/picoclaw-workspace",
|
||||||
|
|
@ -557,6 +557,8 @@ go test ./pkg/config -run TestSecurityConfig
|
||||||
|
|
||||||
### Step 1: Backup your config
|
### 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
|
```bash
|
||||||
cp ~/.picoclaw/config.json ~/.picoclaw/config.json.backup
|
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)
|
### Step 8: Clean up (optional)
|
||||||
|
|
||||||
If everything works, you can delete the backup:
|
If everything works, you can delete the backups:
|
||||||
```bash
|
```bash
|
||||||
rm ~/.picoclaw/config.json.backup
|
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
|
## 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
|
```json
|
||||||
{
|
{
|
||||||
"model_list": [
|
"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://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_key": "sk-key2" }
|
{ "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ũ
|
#### 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
|
### Kiến Trúc Provider
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,19 +92,19 @@ picoclaw onboard
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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"
|
"api_base":"https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/claude-sonnet-4.6",
|
"model": "anthropic/claude-sonnet-4.6",
|
||||||
"api_key": "your-anthropic-key"
|
"api_keys": ["your-anthropic-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -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_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "glm-4.7",
|
||||||
"model": "zhipu/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_name": "deepseek-chat",
|
||||||
"model": "deepseek/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"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_name": "my-custom-model",
|
||||||
"model": "openai/custom-model",
|
"model": "openai/custom-model",
|
||||||
"api_base": "https://my-proxy.com/v1",
|
"api_base": "https://my-proxy.com/v1",
|
||||||
"api_key": "sk-...",
|
"api_keys": ["sk-..."],
|
||||||
"request_timeout": 300
|
"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_name": "lite-gpt4",
|
||||||
"model": "litellm/lite-gpt4",
|
"model": "litellm/lite-gpt4",
|
||||||
"api_base": "http://localhost:4000/v1",
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"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`
|
#### 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ợ):**
|
**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
|
```json
|
||||||
{
|
{
|
||||||
|
"version": 2,
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-key"
|
"api_keys": ["your-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"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.
|
- 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.
|
- Đườ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>
|
<details>
|
||||||
<summary><b>Zhipu</b></summary>
|
<summary><b>Zhipu</b></summary>
|
||||||
|
|
|
||||||
|
|
@ -386,22 +386,22 @@ Agent 读取 HEARTBEAT.md
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -421,7 +421,7 @@ Agent 读取 HEARTBEAT.md
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "glm-4.7",
|
||||||
"model": "zhipu/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_name": "deepseek-chat",
|
||||||
"model": "deepseek/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"api_base": "https://api.anthropic.com"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -514,7 +514,7 @@ Agent 读取 HEARTBEAT.md
|
||||||
"model_name": "my-custom-model",
|
"model_name": "my-custom-model",
|
||||||
"model": "openai/custom-model",
|
"model": "openai/custom-model",
|
||||||
"api_base": "https://my-proxy.com/v1",
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"api_base": "https://api2.example.com/v1",
|
||||||
"api_key": "sk-key2"
|
"api_keys": ["sk-key2"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -547,7 +547,7 @@ PicoClaw 只剥离最外层的 `litellm/` 前缀再发送请求,因此 `litell
|
||||||
|
|
||||||
#### 从旧版 `providers` 配置迁移
|
#### 从旧版 `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 架构
|
### Provider 架构
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,19 +94,19 @@ picoclaw onboard
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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"
|
"api_base":"https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "your-api-key",
|
"api_keys": ["your-api-key"],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "claude-sonnet-4.6",
|
"model_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/claude-sonnet-4.6",
|
"model": "anthropic/claude-sonnet-4.6",
|
||||||
"api_key": "your-anthropic-key"
|
"api_keys": ["your-anthropic-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
|
|
|
||||||
|
|
@ -75,22 +75,22 @@
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/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_name": "gpt-5.4",
|
||||||
"model": "openai/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_name": "claude-sonnet-4.6",
|
||||||
"model": "anthropic/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_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-zhipu-key"
|
"api_keys": ["your-zhipu-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -113,7 +113,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "voice-gemini",
|
"model_name": "voice-gemini",
|
||||||
"model": "gemini/gemini-2.5-flash",
|
"model": "gemini/gemini-2.5-flash",
|
||||||
"api_key": "your-gemini-key"
|
"api_keys": ["your-gemini-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"voice": {
|
"voice": {
|
||||||
|
|
@ -136,7 +136,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_key": "sk-..."
|
"api_keys": ["sk-..."]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "ark-code-latest",
|
"model_name": "ark-code-latest",
|
||||||
"model": "volcengine/ark-code-latest",
|
"model": "volcengine/ark-code-latest",
|
||||||
"api_key": "sk-..."
|
"api_keys": ["sk-..."]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/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_name": "deepseek-chat",
|
||||||
"model": "deepseek/deepseek-chat",
|
"model": "deepseek/deepseek-chat",
|
||||||
"api_key": "sk-..."
|
"api_keys": ["sk-..."]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -190,7 +190,7 @@
|
||||||
{
|
{
|
||||||
"model_name": "claude-opus-4-6",
|
"model_name": "claude-opus-4-6",
|
||||||
"model": "anthropic-messages/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"
|
"api_base": "https://api.anthropic.com"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -218,7 +218,7 @@
|
||||||
"model_name": "my-custom-model",
|
"model_name": "my-custom-model",
|
||||||
"model": "openai/custom-model",
|
"model": "openai/custom-model",
|
||||||
"api_base": "https://my-proxy.com/v1",
|
"api_base": "https://my-proxy.com/v1",
|
||||||
"api_key": "sk-...",
|
"api_keys": ["sk-..."],
|
||||||
"request_timeout": 300
|
"request_timeout": 300
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -230,7 +230,7 @@
|
||||||
"model_name": "lite-gpt4",
|
"model_name": "lite-gpt4",
|
||||||
"model": "litellm/lite-gpt4",
|
"model": "litellm/lite-gpt4",
|
||||||
"api_base": "http://localhost:4000/v1",
|
"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_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api1.example.com/v1",
|
"api_base": "https://api1.example.com/v1",
|
||||||
"api_key": "sk-key1"
|
"api_keys": ["sk-key1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gpt-5.4",
|
"model_name": "gpt-5.4",
|
||||||
"model": "openai/gpt-5.4",
|
"model": "openai/gpt-5.4",
|
||||||
"api_base": "https://api2.example.com/v1",
|
"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_name": "qwen-main",
|
||||||
"model": "openai/qwen3.5:cloud",
|
"model": "openai/qwen3.5:cloud",
|
||||||
"api_base": "https://api.example.com/v1",
|
"api_base": "https://api.example.com/v1",
|
||||||
"api_key": "sk-main"
|
"api_keys": ["sk-main"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "deepseek-backup",
|
"model_name": "deepseek-backup",
|
||||||
"model": "deepseek/deepseek-chat",
|
"model": "deepseek/deepseek-chat",
|
||||||
"api_key": "sk-backup-1"
|
"api_keys": ["sk-backup-1"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model_name": "gemini-backup",
|
"model_name": "gemini-backup",
|
||||||
"model": "gemini/gemini-2.5-flash",
|
"model": "gemini/gemini-2.5-flash",
|
||||||
"api_key": "sk-backup-2"
|
"api_keys": ["sk-backup-2"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
@ -300,7 +300,7 @@ PicoClaw 在发送请求前仅去除外层 `litellm/` 前缀,因此 `litellm/l
|
||||||
|
|
||||||
#### 从旧的 `providers` 配置迁移
|
#### 从旧的 `providers` 配置迁移
|
||||||
|
|
||||||
旧的 `providers` 配置格式**已弃用**,但为向后兼容仍支持。
|
旧的 `providers` 配置格式**已弃用**,V2 中已移除。现有 V0/V1 配置会自动迁移。
|
||||||
|
|
||||||
**旧配置(已弃用):**
|
**旧配置(已弃用):**
|
||||||
|
|
||||||
|
|
@ -325,11 +325,12 @@ PicoClaw 在发送请求前仅去除外层 `litellm/` 前缀,因此 `litellm/l
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"version": 2,
|
||||||
"model_list": [
|
"model_list": [
|
||||||
{
|
{
|
||||||
"model_name": "glm-4.7",
|
"model_name": "glm-4.7",
|
||||||
"model": "zhipu/glm-4.7",
|
"model": "zhipu/glm-4.7",
|
||||||
"api_key": "your-key"
|
"api_keys": ["your-key"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"agents": {
|
"agents": {
|
||||||
|
|
|
||||||
|
|
@ -107,11 +107,13 @@ func Run(debug bool, homePath, configPath string, allowEmptyStartup bool) error
|
||||||
logger.Fatalf("config pre-check failed: %v", err)
|
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 {
|
if debug {
|
||||||
logger.SetLevel(logger.DEBUG)
|
logger.SetLevel(logger.DEBUG)
|
||||||
fmt.Println("🔍 Debug mode enabled")
|
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.
|
// 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")
|
logger.Warn("Config reload skipped: another reload is in progress")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err := executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup)
|
err := executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup, debug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Config reload failed: %v", err)
|
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)
|
runningServices.reloading.Store(false)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup)
|
err = executeReload(ctx, agentLoop, newCfg, &provider, runningServices, msgBus, allowEmptyStartup, debug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Manual reload failed: %v", err)
|
logger.Errorf("Manual reload failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -243,12 +245,13 @@ func executeReload(
|
||||||
runningServices *services,
|
runningServices *services,
|
||||||
msgBus *bus.MessageBus,
|
msgBus *bus.MessageBus,
|
||||||
allowEmptyStartup bool,
|
allowEmptyStartup bool,
|
||||||
|
debug bool,
|
||||||
) error {
|
) error {
|
||||||
defer runningServices.reloading.Store(false)
|
defer runningServices.reloading.Store(false)
|
||||||
|
|
||||||
overridePicoToken(newCfg, runningServices.authToken)
|
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(
|
func createStartupProvider(
|
||||||
|
|
@ -420,6 +423,7 @@ func handleConfigReload(
|
||||||
runningServices *services,
|
runningServices *services,
|
||||||
msgBus *bus.MessageBus,
|
msgBus *bus.MessageBus,
|
||||||
allowEmptyStartup bool,
|
allowEmptyStartup bool,
|
||||||
|
debug bool,
|
||||||
) error {
|
) error {
|
||||||
logger.Info("🔄 Config file changed, reloading...")
|
logger.Info("🔄 Config file changed, reloading...")
|
||||||
|
|
||||||
|
|
@ -468,6 +472,14 @@ func handleConfigReload(
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info(" ✓ Provider, configuration, and services reloaded successfully (thread-safe)")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue