a2d238d92e
Make Zed Agent closer to Cursor: deterministic DevOps path index, live Traefik port probe before blind edits, stop-after-edit, and quieter Russian progress.
159 lines
6.8 KiB
Markdown
159 lines
6.8 KiB
Markdown
# EventHub AI Router — Zed gateway + LiteLLM on IFT
|
||
|
||
OpenAI-compatible endpoint for [Zed](https://zed.dev) with tier/lane routing, optional **hierarchical** orchestration (**Qwen3.8-Max** plan → cheap **Qwen3-Coder-30B** workers/executor → conditional **DeepSeek V3.2** verify), GigaChat classifier, VLESS VPN, Grafana, LiteLLM Admin UI.
|
||
|
||
Цель: качество правок близко к Cursor при экономии бюджета — дорогие модели только на plan/hard/verify, рутина на дешёвом coder.
|
||
|
||
## URLs (IFT, Calentiq TLS)
|
||
|
||
| Service | URL |
|
||
|---------|-----|
|
||
| **Zed API** | `https://ai-router.ift.calentiq.com/v1` |
|
||
| **LiteLLM Admin UI** | `https://litellm.ift.calentiq.com/ui` (login: `admin` / `LITELLM_MASTER_KEY`) |
|
||
| **Grafana** | `https://grafana.ift.calentiq.com` |
|
||
| Legacy (self-signed) | `https://ai-router.ift.eventhub.local/v1` |
|
||
|
||
## Клиент (канон): Zed Agent → AiRouter
|
||
|
||
**Primary:** встроенный Zed Agent (OpenAI-compatible), не Claude Code / Codex ACP — те обходят Novita-бюджет.
|
||
|
||
Пример `settings.json` (Zed 2026 openai-compatible provider):
|
||
|
||
```json
|
||
{
|
||
"language_models": {
|
||
"openai_compatible": {
|
||
"AiRouter": {
|
||
"api_url": "https://ai-router.ift.calentiq.com/v1",
|
||
"available_models": [
|
||
{
|
||
"name": "smart-router",
|
||
"display_name": "EventHub smart-router",
|
||
"max_tokens": 16384,
|
||
"max_output_tokens": 4096,
|
||
"capabilities": {
|
||
"tools": true,
|
||
"images": false,
|
||
"parallel_tool_calls": false
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"agent": {
|
||
"default_model": {
|
||
"provider": "AiRouter",
|
||
"model": "smart-router"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
API key: `ROUTER_API_KEY` в Agent Settings / provider UI.
|
||
|
||
Checklist:
|
||
|
||
- модель `smart-router`, `capabilities.tools: true`
|
||
- профиль **Write** (tools on) для правок файлов
|
||
- Ask без tools → text hierarchical / lane routing
|
||
|
||
Optional headers / metadata:
|
||
|
||
- `X-AI-Quality: auto|economy|balanced|max` — `max` поднимает hard на Qwen3.8-Max
|
||
- `X-AI-Orchestrate: off|auto|force` — hierarchical (`force` always; `auto` на COMPLEX/REASONING; `economy` quality пропускает hierarchical)
|
||
|
||
### Запасной клиент (тот же AiRouter)
|
||
|
||
Если native Zed Agent loop хрупкий: **OpenCode** или **Cline** с `base_url=https://ai-router.ift.calentiq.com/v1` и тем же ключом — иерархия бюджета сохраняется. Claude Code / Codex ACP в Zed — **не** primary (свой биллинг).
|
||
|
||
### Zed Agent Write + hierarchical
|
||
|
||
Если в запросе есть `tools` (профиль **Write**), gateway **не** подменяет agent-цикл текстовым синтезом:
|
||
|
||
1. **Max**-планировщик строит план (`paths` / `edit_goal` / `constraints`) → при `plan_confirm: true` ждёт **утвердить / правка / отмена**
|
||
2. После `ok` — **Qwen3-Coder-30B** executor получает `tools` и отвечает **`tool_calls`** (Zed сам правит файлы)
|
||
3. Пока в истории есть `role: tool` / `tool_calls` — только executor; при повторных fail edit → escalate на DeepSeek (без synthetic tool_calls)
|
||
|
||
Чат без tools (Ask / Minimal) по-прежнему может идти через полный text hierarchical.
|
||
|
||
### Progress в Zed (hierarchical)
|
||
|
||
При `stream: true` (дефолт Zed) gateway сразу стримит блок **«Ход выполнения»**: план → волны workers → verify → синтез, затем `---` и финальный ответ. Конфиг: `hierarchical.stream_progress` / `progress_in_content` в `config/orchestration.yaml`.
|
||
|
||
## Provider profiles (`PRIMARY_PROVIDER`)
|
||
|
||
| Profile | Keys | VPN | Role |
|
||
|---------|------|-----|------|
|
||
| **`hybrid`** (default) | `NOVITA_API_KEY` | workers direct | Novita workers + **Novita** plan/verify |
|
||
| **`openrouter`** | `OPENROUTER_API_KEY` | **VPN** | Reserve (`:free` / non-Anthropic); Claude/OpenAI/Google may be region-blocked |
|
||
|
||
GigaChat (`GIGACHAT_CREDENTIALS`) — classifier. Optional: Groq / Gemini / Grok / Anthropic (если биллинг заработает).
|
||
|
||
```bash
|
||
bash scripts/switch-provider.sh hybrid
|
||
bash scripts/deploy.sh
|
||
# VPN нужен только для OpenRouter / Grok / Anthropic — не для Novita hierarchical
|
||
```
|
||
|
||
## Hierarchical flow (COMPLEX / force)
|
||
|
||
1. **`novita-planner`** (**Qwen3.8-Max**) — JSON subtasks (`simple` | `medium_code` | `hard`) + `paths` / `edit_goal`
|
||
2. **Workers / agent executor**: `simple` → Llama (text only); `medium_code` → **Qwen3-Coder-30B**; `hard` → **DeepSeek V3.2**; `quality=max` hard → **Qwen3.8-Max**
|
||
3. Checks; **`novita-verifier`** (DeepSeek V3.2) только на fail / hard / `quality=max`
|
||
4. **Synthesize** (text path) на `a-medium-code`
|
||
|
||
> Anthropic / OpenRouter-Claude недоступны при RU billing — plan/verify идут через Novita.
|
||
|
||
Response `x_router_meta.mode=hierarchical` / `hierarchical_agent` with `worker_calls`, `executor_model`, `role_cost`, `verify_skipped`, etc.
|
||
|
||
## Architecture
|
||
|
||
- **Gateway** (`router/`) — classify, lanes A/B/C, hierarchical runner, Redis session
|
||
- **LiteLLM** — named models + Auto Router (`smart-router-internal`)
|
||
- **PostgreSQL** — spend logs / Admin UI
|
||
- **Redis** — cache + session
|
||
- **VPN** — `vless-proxy` (Anthropic, OpenRouter, Grok)
|
||
|
||
Config: `config/providers.yaml`, `orchestration.yaml`, `routing_rules.yaml`, `model_matrix.yaml`.
|
||
|
||
## Deploy on IFT
|
||
|
||
```bash
|
||
git clone git.sabilin.com/eventhub/EventHubAiRouter /opt/ai-router-stack
|
||
cd /opt/ai-router-stack
|
||
cp .env.example .env # NOVITA_API_KEY; PRIMARY_PROVIDER=hybrid
|
||
bash scripts/deploy.sh
|
||
bash scripts/smoke-test.sh
|
||
SMOKE_HIERARCHICAL=1 bash scripts/smoke-test.sh
|
||
```
|
||
|
||
Swarm secrets: `novita_api_key`, optional `anthropic_api_key` / `openrouter_api_key`, `gigachat_credentials`, …
|
||
|
||
**`LITELLM_SALT_KEY`** — generate once, never change after first deploy.
|
||
|
||
## VPN
|
||
|
||
```bash
|
||
bash scripts/vpn-enable.sh
|
||
bash scripts/vpn-disable.sh
|
||
```
|
||
|
||
| Provider | Host | Route |
|
||
|----------|------|-------|
|
||
| Novita | `api.novita.ai` | **direct** (NO_PROXY) |
|
||
| Groq / Gemini / GigaChat | … | **direct** |
|
||
| **Anthropic** | `api.anthropic.com` | **VPN** (App unavailable without) |
|
||
| **OpenRouter** | `openrouter.ai` | **VPN** |
|
||
| **xAI Grok** | `api.x.ai` | **VPN** |
|
||
|
||
Canon: `scripts/no-proxy-default.sh`.
|
||
|
||
## LLM classifier (GigaChat)
|
||
|
||
`CLASSIFIER_MODE=hybrid|heuristic|llm` — see `config/orchestration.yaml`. Freemium GigaChat for personal test.
|
||
|
||
## Scripts
|
||
|
||
See `scripts/` — `deploy.sh`, `smoke-test.sh`, `gen-litellm-config.py`, `switch-provider.sh`, VPN helpers.
|