Phase 1: Add 5 SKILL.md files to .agents/skills/ - backlog-sync: plans/ markdown ↔ DB contract - convoy-lifecycle: status machine, gates, MCP tools, checkpoints - model-routing: role→model via OmniRoute profiles + registry - code-review-two-axis: Standards vs Spec parallel sub-agent review - tdd-red-green: TDD loop with seams Phase 2: OmniRoute operational docs - docs/omniroute-role-mapping.md: profile→role matrix, convoy examples - docs/omniroute-setup.md: step-by-step operator guide Phase 3: Worker OpenHands integration - apps/worker/src/session/load-skills.ts: loads .agents/skills/ - apps/worker/src/session/openhands-session.ts: agent-server REST adapter - apps/worker/src/executor/index.ts: createOpenHandsExecutor() factory - apps/worker/src/env.ts: WORKER_SESSION_TYPE, AGENT_SERVER_URL, SESSION_API_KEY - apps/worker/src/handle-job.ts: routes to OpenHands when configured - apps/worker/src/session/minimal-session.ts: injects skills into system prompt - packages/shared/src/worker/index.ts: re-export WorkerExecutorOptions
313 lines
10 KiB
Markdown
313 lines
10 KiB
Markdown
# OmniRoute Role Mapping
|
|
|
|
**Status:** Draft
|
|
**Date:** 2026-09-07
|
|
**Purpose:** Map Echodo pipeline roles to OmniRoute profiles so the orchestrator/worker can route each role to the right model automatically.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
OmniRoute is an OpenAI-compatible gateway that fronts **929+ models** with a small set of **dynamic profiles**. Instead of configuring individual model IDs per role, we configure **profiles** that OmniRoute resolves to a live model endpoint:
|
|
|
|
| Profile | Purpose | Typical Resolves To |
|
|
|---------|---------|---------------------|
|
|
| `auto` | Fastest general-purpose | Small/fast model |
|
|
| `auto/coding` | Code-specialized | Coding-tuned model |
|
|
| `auto/coding-pro` | Complex code tasks, reviews | High-quality coding model |
|
|
| `auto/reasoning` | Planning, reasoning, architecture | Reasoning-tuned model |
|
|
| `auto/reasoning-pro` | Complex reasoning, escalation | Top reasoning model |
|
|
|
|
OmniRoute is integrated into Echodo as an **`openai_compatible` provider** — the same way Ollama and MLX are, but pointing at the OmniRoute base URL and using profile names as `modelId`.
|
|
|
|
---
|
|
|
|
## Profile → Role Matrix
|
|
|
|
### Recommended Defaults
|
|
|
|
| Role ID | OmniRoute Profile | Why |
|
|
|---------|-------------------|-----|
|
|
| `role-conductor` | `auto/reasoning` | Convoy kickoff: classify, skip flags, success metric |
|
|
| `role-architect` | `auto/reasoning` | Design, brief decomposition, architecture |
|
|
| `role-architect` (escalation) | `auto/reasoning-pro` | Complex/hard design problems |
|
|
| `role-ia-architect` | `auto/reasoning` | Information architecture planning |
|
|
| `role-implementer` | `auto/coding` | Default code implementation |
|
|
| `role-implementer` (escalation) | `auto/coding-pro` | After 2 failed attempts |
|
|
| `role-reviewer` | `auto/coding-pro` | Two-axis code review needs quality |
|
|
| `role-ux-reviewer` | `auto/coding` | UI review |
|
|
| `role-doc-writer` | `auto/coding` | Docs are simpler than code |
|
|
| `role-design-system-auditor` | `auto/coding` | Design system review |
|
|
| `role-a11y-auditor` | `auto/coding` | Accessibility review |
|
|
| `gate-copilot` | `auto/reasoning` | Summarize for human gate |
|
|
| `chat-default` | `auto` | Web chat default |
|
|
|
|
### OmniRoute-Only Workspace (Simple Setup)
|
|
|
|
If a workspace does **not** use local models (no Ollama/MLX), just create **5 OmniRoute providers** (one per profile) and route every role to the relevant profile:
|
|
|
|
```yaml
|
|
# Workspace model providers (via Settings → AI → Providers)
|
|
- alias: omni-auto
|
|
provider_type: openai_compatible
|
|
model_id: auto
|
|
base_url: ${OMNIROUTE_BASE_URL} # e.g. https://omniroute.example.com/v1
|
|
api_key_env_var: OMNIROUTE_API_KEY
|
|
metadata: { tier: auto, provider: omniroute }
|
|
|
|
- alias: omni-coding
|
|
provider_type: openai_compatible
|
|
model_id: auto/coding
|
|
base_url: ${OMNIROUTE_BASE_URL}
|
|
api_key_env_var: OMNIROUTE_API_KEY
|
|
metadata: { tier: auto, provider: omniroute }
|
|
|
|
- alias: omni-coding-pro
|
|
provider_type: openai_compatible
|
|
model_id: auto/coding-pro
|
|
base_url: ${OMNIROUTE_BASE_URL}
|
|
api_key_env_var: OMNIROUTE_API_KEY
|
|
metadata: { tier: auto, provider: omniroute }
|
|
|
|
- alias: omni-reasoning
|
|
provider_type: openai_compatible
|
|
model_id: auto/reasoning
|
|
base_url: ${OMNIROUTE_BASE_URL}
|
|
api_key_env_var: OMNIROUTE_API_KEY
|
|
metadata: { tier: auto, provider: omniroute }
|
|
|
|
- alias: omni-reasoning-pro
|
|
provider_type: openai_compatible
|
|
model_id: auto/reasoning-pro
|
|
base_url: ${OMNIROUTE_BASE_URL}
|
|
api_key_env_var: OMNIROUTE_API_KEY
|
|
metadata: { tier: auto, provider: omniroute }
|
|
```
|
|
|
|
Then map roles:
|
|
|
|
| Role ID | Alias |
|
|
|---------|-------|
|
|
| `role-conductor` | `omni-reasoning` |
|
|
| `role-architect` | `omni-reasoning` |
|
|
| `role-ia-architect` | `omni-reasoning` |
|
|
| `role-implementer` | `omni-coding` |
|
|
| `role-reviewer` | `omni-coding-pro` |
|
|
| `role-ux-reviewer` | `omni-coding` |
|
|
| `role-doc-writer` | `omni-coding` |
|
|
| `role-design-system-auditor` | `omni-coding` |
|
|
| `role-a11y-auditor` | `omni-coding` |
|
|
| `gate-copilot` | `omni-reasoning` |
|
|
| `chat-default` | `omni-auto` |
|
|
|
|
### Hybrid Setup (Local + OmniRoute)
|
|
|
|
For a homelab that wants free local inference AND cloud fallback:
|
|
|
|
| Role ID | Default | Escalation |
|
|
|---------|---------|------------|
|
|
| `role-conductor` | `omni-reasoning` (cloud) | — |
|
|
| `role-architect` | `omni-reasoning-pro` (cloud) | — |
|
|
| `role-implementer` | `qwen-coder-mlx` (local) | `omni-coding-pro` (cloud) |
|
|
| `role-reviewer` | `qwen-coder-mlx` (local) | `omni-coding-pro` (cloud) |
|
|
| `role-ux-reviewer` | `qwen-coder-mlx` (local) | `omni-coding` |
|
|
| `role-doc-writer` | `qwen-coder-mlx` (local) | `omni-coding` |
|
|
| `gate-copilot` | `omni-reasoning` (cloud) | — |
|
|
| `chat-default` | `omni-auto` (cloud) | — |
|
|
|
|
Hardcoded in convoy `model_policy`:
|
|
|
|
```yaml
|
|
model_policy:
|
|
roles:
|
|
role-conductor: omni-reasoning
|
|
role-architect: omni-reasoning-pro
|
|
role-implementer: qwen-coder-mlx
|
|
role-reviewer: omni-coding-pro
|
|
role-doc-writer: qwen-coder-mlx
|
|
escalate_to: omni-coding-pro
|
|
```
|
|
|
|
---
|
|
|
|
## How Resolution Works
|
|
|
|
Echodo's `resolveModel(workspaceId, roleId, ctx?)` (pure function in `packages/shared/src/model-routing/resolve-model.ts`) applies this order:
|
|
|
|
```
|
|
1. Brief override → recommended_model in .convoys/<slug>/brief-*.md
|
|
2. Convoy override → model_policy.roles[roleId] in .convoys/<slug>.md
|
|
3. Workspace route → role_model_routes row in DB
|
|
4. Pipeline template → PIPELINE_DEFAULT_ROLE_ROUTES (shipped defaults)
|
|
```
|
|
|
|
The resolved `ResolvedModel` object is passed to the worker. The worker creates a Vercel AI SDK `LanguageModel` via `createLanguageModelFromResolved` (`packages/ai/src/providers/resolve-client.ts`).
|
|
|
|
**Because OmniRoute is `openai_compatible`:** it flows through the existing `case "openai_compatible"` branch, so **no code change is required** in `resolve-client.ts` — it uses `createOpenAI({ apiKey, baseURL })` with `modelId` = profile name.
|
|
|
|
---
|
|
|
|
## Resolved Model Example
|
|
|
|
For `role-implementer` → `omni-coding`:
|
|
|
|
```typescript
|
|
{
|
|
alias: "omni-coding",
|
|
providerType: "openai_compatible",
|
|
modelId: "auto/coding",
|
|
baseUrl: "https://omniroute.example.com/v1",
|
|
apiKey: "sk-...", // read from OMNIROUTE_API_KEY at runtime
|
|
tier: "auto"
|
|
}
|
|
```
|
|
|
|
The worker's `createLanguageModelFromResolved` builds:
|
|
|
|
```typescript
|
|
const openai = createOpenAI({
|
|
apiKey: "sk-...",
|
|
baseURL: "https://omniroute.example.com/v1",
|
|
});
|
|
const model = openai("auto/coding");
|
|
```
|
|
|
|
OmniRoute then routes `auto/coding` to an appropriate live model per its own logic.
|
|
|
|
---
|
|
|
|
## Convoy Frontmatter
|
|
|
|
### OmniRoute-Only Convoy
|
|
|
|
```yaml
|
|
---
|
|
name: feature-auth
|
|
classification: server-only
|
|
success_metric: POST /auth/register works and returns 201
|
|
skip:
|
|
- ia
|
|
- ux
|
|
- design
|
|
- a11y
|
|
- visual
|
|
- smoke
|
|
status: draft
|
|
created: 2026-09-07
|
|
model_policy:
|
|
roles:
|
|
role-conductor: omni-reasoning
|
|
role-architect: omni-reasoning
|
|
role-implementer: omni-coding
|
|
role-reviewer: omni-coding-pro
|
|
escalate_to: omni-coding-pro
|
|
---
|
|
```
|
|
|
|
### Hybrid Convoy
|
|
|
|
```yaml
|
|
---
|
|
name: feature-local-first
|
|
model_policy:
|
|
roles:
|
|
role-conductor: omni-reasoning
|
|
role-architect: omni-reasoning
|
|
role-implementer: qwen-coder-mlx
|
|
role-reviewer: qwen-coder-mlx
|
|
escalate_to: omni-coding-pro
|
|
---
|
|
```
|
|
|
|
### Brief Override
|
|
|
|
```yaml
|
|
---
|
|
brief: complex-db-migration
|
|
recommended_model: omni-reasoning-pro # overrides convoy + workspace
|
|
model_tier: auto
|
|
---
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Where | Purpose |
|
|
|----------|-------|---------|
|
|
| `OMNIROUTE_BASE_URL` | Worker + web process env | OmniRoute endpoint (must end in `/v1`) |
|
|
| `OMNIROUTE_API_KEY` | Worker + web process env | Autogenerated by OpenHands/OmniRoute |
|
|
|
|
These names are **referenced** in `model_providers.apiKeyEnvVar` — the actual values live in deploy env (Coolify/Dokploy/Mac), never in the DB or committed files.
|
|
|
|
---
|
|
|
|
## Testing the Mapping
|
|
|
|
### Via tRPC
|
|
|
|
```typescript
|
|
// Debug endpoint: resolve a role to a model
|
|
resolveForRole({ roleId: "role-implementer" })
|
|
// → { ok: true, model: { alias: "omni-coding", modelId: "auto/coding", ... } }
|
|
```
|
|
|
|
### Via MCP testProvider
|
|
|
|
```typescript
|
|
testProvider({ alias: "omni-coding" })
|
|
// → { success: true, latency_ms: 320, model: "auto/coding" }
|
|
```
|
|
|
|
### Via direct OpenAI-compatible call
|
|
|
|
```bash
|
|
curl ${OMNIROUTE_BASE_URL}/models \
|
|
-H "Authorization: Bearer ${OMNIROUTE_API_KEY}"
|
|
# → List of available models (929+ for OmniRoute)
|
|
```
|
|
|
|
---
|
|
|
|
## Default Pipeline Template After OmniRoute
|
|
|
|
Update `PIPELINE_DEFAULT_ROLE_ROUTES` in `packages/shared/src/model-routing/constants.ts` if OmniRoute is the default for new workspaces:
|
|
|
|
```typescript
|
|
export const PIPELINE_DEFAULT_ROLE_ROUTES: Readonly<Record<string, string>> = {
|
|
"role-conductor": "omni-reasoning",
|
|
"role-architect": "omni-reasoning",
|
|
"role-ia-architect": "omni-reasoning",
|
|
"role-ux-reviewer": "omni-coding",
|
|
"role-implementer": "omni-coding",
|
|
"role-reviewer": "omni-coding-pro",
|
|
"role-design-system-auditor": "omni-coding",
|
|
"role-a11y-auditor": "omni-coding",
|
|
"role-doc-writer": "omni-coding",
|
|
"gate-copilot": "omni-reasoning",
|
|
"chat-default": "omni-auto",
|
|
};
|
|
```
|
|
|
|
*(Note: changing the template only affects new workspaces — existing workspace routes in the DB take precedence.)*
|
|
|
|
---
|
|
|
|
## Open Questions
|
|
|
|
1. **Should the template switch to OmniRoute aliases by default, or stay Claude/Qwen and let operators pick OmniRoute?** Recommend: keep Claude/Qwen seed, document OmniRoute as an option until it's proven.
|
|
2. **Does OmniRoute support streaming + function calling?** Needed for the worker tool loop. Verify via a live test before routing implementer roles to OmniRoute.
|
|
3. **Rate limits / cost:** OmniRoute `auto/*` profiles rotate models — need token/cost tracking in `agent_runs` to monitor spend.
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- **Resolver:** `packages/shared/src/model-routing/resolve-model.ts`
|
|
- **Schemas:** `packages/shared/src/model-routing/schemas.ts`
|
|
- **Constants:** `packages/shared/src/model-routing/constants.ts`
|
|
- **Client builder:** `packages/ai/src/providers/resolve-client.ts`
|
|
- **Provider schema:** `packages/database/src/schema/model_providers.ts`
|
|
- **Route schema:** `packages/database/src/schema/role_model_routes.ts`
|
|
- **ADR:** `docs/adr-model-routing.md`
|
|
- **Skill:** `.agents/skills/model-routing/SKILL.md`
|