Skip to content
Closed
184 changes: 169 additions & 15 deletions assets/oh-my-opencode.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@
"$schema": {
"type": "string"
},
"model": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"disabled_mcps": {
"type": "array",
"items": {
Expand Down Expand Up @@ -77,7 +90,8 @@
"delegate-task-retry",
"prometheus-md-only",
"start-work",
"atlas"
"atlas",
"smart-failover"
]
}
},
Expand All @@ -98,7 +112,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -224,7 +248,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -350,7 +384,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -476,7 +520,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -602,7 +656,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -728,7 +792,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -854,7 +928,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -980,7 +1064,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -1106,7 +1200,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -1232,7 +1336,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -1358,7 +1472,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -1484,7 +1608,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -1610,7 +1744,17 @@
"type": "object",
"properties": {
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down Expand Up @@ -1746,7 +1890,17 @@
"type": "string"
},
"model": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"variant": {
"type": "string"
Expand Down
59 changes: 59 additions & 0 deletions docs/features/smart-failover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Smart Provider Failover

## 1. Overview
In multi-model environments, providers often hit **429 (Rate Limits)**, **Insufficient Balance**, or **Quota Exhaustion**.
The Smart Failover system provides an automated detection and recovery mechanism, ensuring uninterrupted service by switching to healthy fallback models instantly.

## 2. Key Features
- **Pipe Syntax (`|`)**: Minimalist fallback chain definitions.
- **Array Syntax (`string[]`)**: Equivalent to pipe syntax, easier to edit.
- **Instant Failover**: Aborts OpenCode's internal retry loops to trigger immediate model swapping.
- **Error Diagnosis (Best-Effort)**: Classifies common failures (rate-limit, quota, balance) via pattern matching.
- **Guardrails**:
- **Context Compatibility**: Skips fallbacks with insufficient context windows.
- **Probation Recovery**: After a cooldown elapses, a model becomes eligible again (PROBATION) and is cleared back to healthy after the session becomes idle.
- **Memory Safety**: Automatic cleanup upon session deletion.

## 3. Configuration
Smart Failover is enabled by default (unless you disable the `smart-failover` hook). Configure a fallback chain in `model` to use it.

### 3.1 Model Fallback Chain
You can define the fallback chain using either:

- **Pipe syntax** (string)
- **Array syntax** (string[])

Both forms are equivalent: the first entry is the primary model, and the rest are fallbacks.

### 3.2 Hook Toggle
If you need to disable it, add `smart-failover` to `disabled_hooks` in your `oh-my-opencode.json`.

### Example
```jsonc
{
"model": "openai/gpt-5.2-codex | google/gemini-3-pro"
}
```

### Array Example
```jsonc
{
"model": ["openai/gpt-5.2-codex", "google/gemini-3-pro"]
}
```

`model` can also be configured per-agent (e.g. `agents.Sisyphus.model`) and in category configs. Those locations also accept either pipe syntax or an array.

## 4. Default Behavior
- **Triggers**: Retry-loop detection (`session.status: retry`) and certain session errors (`session.error`) mark the current `provider/model` as unavailable and switch to the next available fallback.
- **Cooling + Backoff**: Retry-loop cooling uses a fixed 5-minute cooldown. Session-error cooling uses exponential backoff based on repeated failures.
- **Locking**: Balance/quota exhaustion signals lock a specific `provider/model` pair (model key) until reset.
- **Fallback Selection**: Only HEALTHY/PROBATION models are eligible; fallbacks with too-small context windows are skipped.

## 5. Limitations
- **Retry-After**: The implementation does not reliably receive response headers in events, so header-based cooldown is best-effort.
- **Probation**: Recovery is approximated by the session becoming idle, not a dedicated health-check request.

## 6. UI/UX
- **Notification**: A yellow toast appears: `⚠️ Switched to google/gemini-3-pro`.
- **Throttling**: Toasts are shown only once per session to prevent UI spam.
Loading
Loading