feat(eltex): add ai-eltex-qwen, effort max by default; fix model pick and /logout-eltex
ai-eltex-qwen: new launcher for the Eltex proxy on Qwen models (Coder-Senior, then Flash-Next), own key eltex_qwen_key, config dir and /logout-eltex-qwen hook branch, 200K window. Effort: ai-glm and ai-eltex start on max via CLI --effort (AI_EFFORT overrides), CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 so long GLM answers are not cut at 32000. EFFORT_MAPPING notes the proxy passes effort for anth-glm-5.3 since 09.10. ai-eltex model pick: no longer falls back to the first /v1/models entry, which twice picked something unusable (zai-glm-5.3 routed to flash, and the access-group name ZAI-Test-Group -> 400). Without anth-glm-5.3 the launcher exits with a clear message; ELTEX_MODEL still overrides. /logout-eltex: Claude Code answered "Unknown command" and never called the logout hook (the built-in /logout bypasses hooks too). ai-eltex now registers commands/logout-eltex.md in its config dir, so the hook receives the command and deletes eltex_key (checked end to end). ru-bypass: resolve exception hosts via ahostsv4 first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
Данные правила применяются ко всем агентам, работающим конкретно в этом репозитории.
|
Данные правила применяются ко всем агентам, работающим конкретно в этом репозитории.
|
||||||
|
|
||||||
1. **Полная перегенерация standalone-скриптов при запуске сетап-скрипта (Full regeneration of standalone scripts on setup run):** При каждом запуске `ai-setup.sh` все генерируемые скрипты в выбранном `BIN_DIR` (`~/bin`, если он уже есть в `PATH`, иначе `~/.local/bin`: `ai-gpt`, `ai-deepseek`, `ai-kimi`, `ai-glm`, `ai-eltex`, `ai-openrouter`, `ai-gemini`, `ai-api-helpers.sh`, `ai-claude`, `claude-gpt-effort-proxy.py`) должны быть **полностью перезаписаны** актуальными версиями. Запрещено выполнять слияние (merge) старого и нового содержимого или дополнение (append). Скрипт обязан привести все генерируемые файлы к эталонному виду, однозначно определяемому текущей конфигурацией.
|
1. **Полная перегенерация standalone-скриптов при запуске сетап-скрипта (Full regeneration of standalone scripts on setup run):** При каждом запуске `ai-setup.sh` все генерируемые скрипты в выбранном `BIN_DIR` (`~/bin`, если он уже есть в `PATH`, иначе `~/.local/bin`: `ai-gpt`, `ai-deepseek`, `ai-kimi`, `ai-glm`, `ai-eltex`, `ai-eltex-qwen`, `ai-openrouter`, `ai-gemini`, `ai-api-helpers.sh`, `ai-claude`, `claude-gpt-effort-proxy.py`) должны быть **полностью перезаписаны** актуальными версиями. Запрещено выполнять слияние (merge) старого и нового содержимого или дополнение (append). Скрипт обязан привести все генерируемые файлы к эталонному виду, однозначно определяемому текущей конфигурацией.
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ low -> high -> max
|
|||||||
`CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1` - иначе Claude Code не считает `glm-5.3` effort-capable
|
`CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1` - иначе Claude Code не считает `glm-5.3` effort-capable
|
||||||
и параметр не уходит.
|
и параметр не уходит.
|
||||||
- Маппинг выполняется на стороне Z.ai API, `ai-glm` локально effort не преобразует.
|
- Маппинг выполняется на стороне Z.ai API, `ai-glm` локально effort не преобразует.
|
||||||
- Дефолт `ai-glm`: `xhigh` (= `high` у GLM), задаётся через CLI `--effort` при каждом запуске.
|
- Дефолт `ai-glm`: `max`, задаётся через CLI `--effort` при каждом запуске.
|
||||||
- Официально Z.ai маппинг `medium`/`xhigh` не документирует. Он выведен замером 2026-09-25:
|
- Официально Z.ai маппинг `medium`/`xhigh` не документирует. Он выведен замером 2026-09-25:
|
||||||
по 3 запроса на уровень, средний `output_tokens` на счётной задаче - `low` ~1.5K,
|
по 3 запроса на уровень, средний `output_tokens` на счётной задаче - `low` ~1.5K,
|
||||||
`medium` ~1.1K, `high` ~3.4K, `xhigh` ~2.8K, `max` ~3.8K. `low`/`medium` уверенно короче
|
`medium` ~1.1K, `high` ~3.4K, `xhigh` ~2.8K, `max` ~3.8K. `low`/`medium` уверенно короче
|
||||||
@@ -130,9 +130,10 @@ AI_EFFORT=high ai-deepseek # включить high
|
|||||||
ai-deepseek # без флага - использовать уровень из settings.json
|
ai-deepseek # без флага - использовать уровень из settings.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Дефолты: `xhigh` для `ai-claude` и `ai-glm`, `high` для остальных Claude Code лаунчеров.
|
Дефолты: `max` для `ai-glm`, `ai-eltex` и `ai-eltex-qwen`, `xhigh` для `ai-claude`, `high` для
|
||||||
`ai-glm` передаёт уровень через CLI `--effort`, поэтому каждый запуск стартует на `xhigh`
|
остальных Claude Code лаунчеров. `ai-glm`, `ai-eltex` и `ai-eltex-qwen` передают уровень через CLI
|
||||||
(или на `AI_EFFORT`), а `/effort` внутри сессии действует до её конца.
|
`--effort`, поэтому каждый запуск стартует на `max` (или на `AI_EFFORT`), а `/effort` внутри сессии
|
||||||
|
действует до её конца.
|
||||||
|
|
||||||
## Рекомендации
|
## Рекомендации
|
||||||
|
|
||||||
@@ -140,5 +141,6 @@ ai-deepseek # без флага - использовать ур
|
|||||||
- `max` effort имеет реальный эффект у Anthropic, DeepSeek и GLM; у Kimi это всё тот же thinking on.
|
- `max` effort имеет реальный эффект у Anthropic, DeepSeek и GLM; у Kimi это всё тот же thinking on.
|
||||||
- `low`/`medium` у Kimi фактически не снижают reasoning; у DeepSeek `low` и у GLM `low`/`medium` дают реальный уровень `low` и экономят бюджет.
|
- `low`/`medium` у Kimi фактически не снижают reasoning; у DeepSeek `low` и у GLM `low`/`medium` дают реальный уровень `low` и экономят бюджет.
|
||||||
- У GLM осмысленно выбирать только `low`, `high` (или `xhigh`) и `max`.
|
- У GLM осмысленно выбирать только `low`, `high` (или `xhigh`) и `max`.
|
||||||
- `ai-eltex`: корпоративный прокси Eltex (LiteLLM) игнорирует effort и thinking, GLM за ним
|
- `ai-eltex`: с 09.10 прокси пропускает effort для `anth-glm-5.3`, маппинг тот же, что у `ai-glm`
|
||||||
думает всегда одинаково. `/effort` и `AI_EFFORT` там ни на что не влияют.
|
(на стороне Z.ai). Как и `ai-glm`, стартует на `max` через CLI `--effort`, `AI_EFFORT=high ai-eltex`
|
||||||
|
работает. У `ai-eltex-qwen` прокси effort игнорирует, `/effort` и `AI_EFFORT` там ни на что не влияют.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ bash setup.sh
|
|||||||
```
|
```
|
||||||
|
|
||||||
Установит лаунчеры
|
Установит лаунчеры
|
||||||
`ai-claude`, `ai-gpt`, `ai-deepseek`, `ai-kimi`, `ai-glm`, `ai-eltex`, `ai-openrouter`, `ai-opencode`, `ai-gemini`, `ai-cursor`
|
`ai-claude`, `ai-gpt`, `ai-deepseek`, `ai-kimi`, `ai-glm`, `ai-eltex`, `ai-eltex-qwen`, `ai-openrouter`, `ai-opencode`, `ai-gemini`, `ai-cursor`
|
||||||
в `~/bin`, если он уже есть в `PATH`, иначе в `~/.local/bin/`, и запишет API-ключи.
|
в `~/bin`, если он уже есть в `PATH`, иначе в `~/.local/bin/`, и запишет API-ключи.
|
||||||
|
|
||||||
После установки, если команды не видны:
|
После установки, если команды не видны:
|
||||||
|
|||||||
@@ -161,7 +161,8 @@ bash tests/test_network.sh # полные тесты
|
|||||||
- `ai-deepseek` - Claude Code через DeepSeek API
|
- `ai-deepseek` - Claude Code через DeepSeek API
|
||||||
- `ai-kimi` - Claude Code через официальный Kimi Code API (`api.kimi.com/coding`)
|
- `ai-kimi` - Claude Code через официальный Kimi Code API (`api.kimi.com/coding`)
|
||||||
- `ai-glm` - Claude Code через Zhipu AI GLM 5.3 API (`api.z.ai/api/anthropic`)
|
- `ai-glm` - Claude Code через Zhipu AI GLM 5.3 API (`api.z.ai/api/anthropic`)
|
||||||
- `ai-eltex` - Claude Code через корпоративный LLM-прокси Eltex (`http://llm.eltex.loc:4000`, LiteLLM, team-подписка GLM). Ключ выдают админы ([дока](https://intdocs.eltex.loc/pages/viewpage.action?pageId=262329878)), по умолчанию модель `anth-glm-5.3` (Anthropic-эндпоинт Z.ai: настоящая GLM 5.3 и картинки), если она есть у ключа, иначе первая из разрешённых ключу; переопределение: `ELTEX_MODEL`, `ELTEX_BASE_URL`
|
- `ai-eltex` - Claude Code через корпоративный LLM-прокси Eltex (`http://llm.eltex.loc:4000`, LiteLLM, team-подписка GLM). Ключ выдают админы ([дока](https://intdocs.eltex.loc/pages/viewpage.action?pageId=262329878)), модель `anth-glm-5.3` (Anthropic-эндпоинт Z.ai: настоящая GLM 5.3 и картинки); если ключу она недоступна, лаунчер выходит с понятной ошибкой; переопределение: `ELTEX_MODEL`, `ELTEX_BASE_URL`
|
||||||
|
- `ai-eltex-qwen` - то же через корпоративный LLM-прокси Eltex, но на моделях Qwen со своим ключом от админов: по умолчанию `Eltex-Coder-Senior` (Qwen3.8-27B), если она есть у ключа, иначе `Eltex-Qwen3_8-Flash-Next`; ключ можно задать в `AI_ELTEX_LLM_API_KEY`, как в онбординге Eltex AI; переопределение: `ELTEX_QWEN_MODEL`, `ELTEX_BASE_URL`
|
||||||
- `ai-openrouter` - Claude Code через OpenRouter (GPT-5.5, Grok, Qwen, GLM 5.2 и др.)
|
- `ai-openrouter` - Claude Code через OpenRouter (GPT-5.5, Grok, Qwen, GLM 5.2 и др.)
|
||||||
- `ai-opencode` - Claude Code через OpenCode Go + routatic-proxy (DeepSeek, Kimi, Qwen, GLM, MiniMax, MiMo)
|
- `ai-opencode` - Claude Code через OpenCode Go + routatic-proxy (DeepSeek, Kimi, Qwen, GLM, MiniMax, MiMo)
|
||||||
- `ai-gemini` - нативный Antigravity CLI `agy`
|
- `ai-gemini` - нативный Antigravity CLI `agy`
|
||||||
@@ -220,6 +221,7 @@ android init
|
|||||||
- `~/.config/ai-setup/kimi_key` - Kimi API key (права 600)
|
- `~/.config/ai-setup/kimi_key` - Kimi API key (права 600)
|
||||||
- `~/.config/ai-setup/glm_key` - Zhipu AI (GLM) API key (права 600)
|
- `~/.config/ai-setup/glm_key` - Zhipu AI (GLM) API key (права 600)
|
||||||
- `~/.config/ai-setup/eltex_key` - ключ корпоративного LLM-прокси Eltex от админов (права 600)
|
- `~/.config/ai-setup/eltex_key` - ключ корпоративного LLM-прокси Eltex от админов (права 600)
|
||||||
|
- `~/.config/ai-setup/eltex_qwen_key` - ключ для моделей Qwen на том же прокси (права 600)
|
||||||
- `~/.config/ai-setup/openrouter_key` - OpenRouter API key (права 600)
|
- `~/.config/ai-setup/openrouter_key` - OpenRouter API key (права 600)
|
||||||
- `~/.config/ai-setup/opencode_key` - OpenCode Go API key (права 600)
|
- `~/.config/ai-setup/opencode_key` - OpenCode Go API key (права 600)
|
||||||
- `~/.config/ai-setup/global_rules.md` - глобальные правила агентов
|
- `~/.config/ai-setup/global_rules.md` - глобальные правила агентов
|
||||||
@@ -228,7 +230,7 @@ android init
|
|||||||
## Права запуска агентов
|
## Права запуска агентов
|
||||||
|
|
||||||
- `ai-gpt` использует `--dangerously-bypass-approvals-and-sandbox`
|
- `ai-gpt` использует `--dangerously-bypass-approvals-and-sandbox`
|
||||||
- `ai-claude`, `ai-deepseek`, `ai-kimi`, `ai-glm`, `ai-eltex`, `ai-openrouter`, `ai-opencode`, `ai-gemini` используют `--dangerously-skip-permissions`
|
- `ai-claude`, `ai-deepseek`, `ai-kimi`, `ai-glm`, `ai-eltex`, `ai-eltex-qwen`, `ai-openrouter`, `ai-opencode`, `ai-gemini` используют `--dangerously-skip-permissions`
|
||||||
- `ai-cursor` использует `--force` (алиас `--yolo`)
|
- `ai-cursor` использует `--force` (алиас `--yolo`)
|
||||||
|
|
||||||
Удобно для локального coding workflow, но это не sandbox для недоверенного кода.
|
Удобно для локального coding workflow, но это не sandbox для недоверенного кода.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright (c) 2026 Виталий Никитенко. MIT License.
|
# Copyright (c) 2026 Виталий Никитенко. MIT License.
|
||||||
# UserPromptSubmit hook: перехватывает /logout, /logout-glm, /logout-eltex, /logout-deepseek, /logout-kimi.
|
# UserPromptSubmit hook: перехватывает /logout, /logout-glm, /logout-eltex, /logout-eltex-qwen, /logout-deepseek, /logout-kimi.
|
||||||
# Удаляет сохранённый API-ключ провайдера, чтобы при следующем запуске лаунчер
|
# Удаляет сохранённый API-ключ провайдера, чтобы при следующем запуске лаунчер
|
||||||
# запросил новый.
|
# запросил новый.
|
||||||
# /logout-glm | /logout-eltex | /logout-deepseek | /logout-kimi — явно указанный провайдер.
|
# /logout-glm | /logout-eltex | /logout-eltex-qwen | /logout-deepseek | /logout-kimi — явно указанный провайдер.
|
||||||
# /logout (без суффикса) — провайдер по AI_LAUNCHER (glm/eltex/deepseek/kimi).
|
# /logout (без суффикса) — провайдер по AI_LAUNCHER (glm/eltex/eltex-qwen/deepseek/kimi).
|
||||||
# Для ai-claude и прочих лаунчеров /logout отступает (exit 0) — пусть работает
|
# Для ai-claude и прочих лаунчеров /logout отступает (exit 0) — пусть работает
|
||||||
# нативный oauth-logout. exit 2 после удаления: LLM не нужна, и нативная
|
# нативный oauth-logout. exit 2 после удаления: LLM не нужна, и нативная
|
||||||
# /logout не пытается сделать бессмысленный oauth-logout для api-key провайдеров.
|
# /logout не пытается сделать бессмысленный oauth-logout для api-key провайдеров.
|
||||||
@@ -18,11 +18,12 @@ target=""
|
|||||||
case "$cmd" in
|
case "$cmd" in
|
||||||
logout-glm) target=glm ;;
|
logout-glm) target=glm ;;
|
||||||
logout-eltex) target=eltex ;;
|
logout-eltex) target=eltex ;;
|
||||||
|
logout-eltex-qwen) target=eltex-qwen ;;
|
||||||
logout-deepseek) target=deepseek ;;
|
logout-deepseek) target=deepseek ;;
|
||||||
logout-kimi) target=kimi ;;
|
logout-kimi) target=kimi ;;
|
||||||
logout)
|
logout)
|
||||||
case "${AI_LAUNCHER:-claude}" in
|
case "${AI_LAUNCHER:-claude}" in
|
||||||
glm|eltex|deepseek|kimi) target="$AI_LAUNCHER" ;;
|
glm|eltex|eltex-qwen|deepseek|kimi) target="$AI_LAUNCHER" ;;
|
||||||
*) exit 0 ;;
|
*) exit 0 ;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
@@ -40,6 +41,10 @@ case "$target" in
|
|||||||
rm -f "$CFG/eltex_key"
|
rm -f "$CFG/eltex_key"
|
||||||
echo "✓ Eltex ключ удалён. Перезапусти ai-eltex, чтобы ввести новый." >&2
|
echo "✓ Eltex ключ удалён. Перезапусти ai-eltex, чтобы ввести новый." >&2
|
||||||
;;
|
;;
|
||||||
|
eltex-qwen)
|
||||||
|
rm -f "$CFG/eltex_qwen_key"
|
||||||
|
echo "✓ Eltex Qwen ключ удалён. Перезапусти ai-eltex-qwen, чтобы ввести новый." >&2
|
||||||
|
;;
|
||||||
deepseek)
|
deepseek)
|
||||||
rm -f "$CFG/deepseek_key"
|
rm -f "$CFG/deepseek_key"
|
||||||
echo "✓ DeepSeek ключ удалён. Перезапусти ai-deepseek, чтобы ввести новый." >&2
|
echo "✓ DeepSeek ключ удалён. Перезапусти ai-deepseek, чтобы ввести новый." >&2
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright (c) 2026 Виталий Никитенко. MIT License.
|
# Copyright (c) 2026 Виталий Никитенко. MIT License.
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# AI Setup - Claude / GPT-5.5 / Grok / Qwen / DeepSeek / Kimi / GLM / Eltex / Gemini
|
# AI Setup - Claude / GPT-5.5 / Grok / Qwen / DeepSeek / Kimi / GLM / Eltex / Eltex Qwen / Gemini
|
||||||
# Запуск: bash ai-setup.sh
|
# Запуск: bash ai-setup.sh
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
@@ -1650,13 +1650,16 @@ export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/glm"
|
|||||||
# opus+sonnet+haiku = glm-5.3. Окно контекста у всех слотов задаёт env
|
# opus+sonnet+haiku = glm-5.3. Окно контекста у всех слотов задаёт env
|
||||||
# CLAUDE_CODE_MAX_CONTEXT_TOKENS ниже — суффикс [1m] больше не нужен.
|
# CLAUDE_CODE_MAX_CONTEXT_TOKENS ниже — суффикс [1m] больше не нужен.
|
||||||
_setup_isolated_config glm opus xhigh ''
|
_setup_isolated_config glm opus xhigh ''
|
||||||
# ai-glm стартует на /effort xhigh через CLI --effort (перебивает effortLevel из
|
# ai-glm стартует на /effort max через CLI --effort (перебивает effortLevel из
|
||||||
# settings.json при каждом запуске). Z.ai сворачивает 5 уровней Claude Code в 3
|
# settings.json при каждом запуске). Z.ai сворачивает 5 уровней Claude Code в 3
|
||||||
# нативных GLM 5.3: low,medium→low; high,xhigh→high; max→max (см. EFFORT_MAPPING.md).
|
# нативных GLM 5.3: low,medium→low; high,xhigh→high; max→max (см. EFFORT_MAPPING.md).
|
||||||
# CLAUDE_CODE_ALWAYS_ENABLE_EFFORT заставляет Claude Code отправлять effort в API
|
# CLAUDE_CODE_ALWAYS_ENABLE_EFFORT заставляет Claude Code отправлять effort в API
|
||||||
# для GLM (иначе glm-5.3 не распознан как effort-capable и параметр не уходит,
|
# для GLM (иначе glm-5.3 не распознан как effort-capable и параметр не уходит,
|
||||||
# а Z.ai по умолчанию берёт max).
|
# а Z.ai по умолчанию берёт max).
|
||||||
# /effort внутри сессии работает; ручной override запуска: AI_EFFORT=max ai-glm.
|
# /effort внутри сессии работает; ручной override запуска: AI_EFFORT=high ai-glm.
|
||||||
|
# CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000: по умолчанию Claude Code просит у glm-5.3
|
||||||
|
# max_tokens=32000 (размышления + ответ), на трудных задачах GLM в них не укладывается
|
||||||
|
# и ответ обрывается. Z.ai принимает и 128000.
|
||||||
# CLAUDE_CODE_DISABLE_1M_CONTEXT НЕ выставляем: GLM 5.3 реально держит 1M контекста,
|
# CLAUDE_CODE_DISABLE_1M_CONTEXT НЕ выставляем: GLM 5.3 реально держит 1M контекста,
|
||||||
# а флаг заставлял бы счётчик ctx: считать окно = 200K (врал в ~5 раз). Прочие
|
# а флаг заставлял бы счётчик ctx: считать окно = 200K (врал в ~5 раз). Прочие
|
||||||
# лаунчеры (deepseek/kimi/openrouter/opencode) флаг оставляют — у них другой контекст
|
# лаунчеры (deepseek/kimi/openrouter/opencode) флаг оставляют — у них другой контекст
|
||||||
@@ -1678,9 +1681,10 @@ ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="GLM 5.3" \
|
|||||||
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="GLM 5.3 — флагман Zhipu AI" \
|
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="GLM 5.3 — флагман Zhipu AI" \
|
||||||
CLAUDE_CODE_SUBAGENT_MODEL=glm-5.3 \
|
CLAUDE_CODE_SUBAGENT_MODEL=glm-5.3 \
|
||||||
CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000 \
|
CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000 \
|
||||||
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 \
|
||||||
CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1 \
|
CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1 \
|
||||||
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
||||||
claude --dangerously-skip-permissions --effort "${AI_EFFORT:-xhigh}" --system-prompt-file "$_PROMPT_FILE" "$@"
|
claude --dangerously-skip-permissions --effort "${AI_EFFORT:-max}" --system-prompt-file "$_PROMPT_FILE" "$@"
|
||||||
GLMEOF
|
GLMEOF
|
||||||
chmod +x "$BIN_DIR/ai-glm"
|
chmod +x "$BIN_DIR/ai-glm"
|
||||||
|
|
||||||
@@ -1770,19 +1774,29 @@ if [ -z "$api_key" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Модель: ELTEX_MODEL, иначе anth-glm-5.3, если она есть у ключа, иначе первая из
|
# Модель: ELTEX_MODEL, иначе anth-glm-5.3 - деплойменты на Anthropic-эндпоинте Z.ai:
|
||||||
# разрешённых ключу (у других ключей другие модели, например Eltex-Coder-Senior из доки).
|
# всегда настоящая glm-5.3 и картинки через серверный analyze_image.
|
||||||
# anth-glm-5.3 - деплойменты на Anthropic-эндпоинте Z.ai: всегда настоящая glm-5.3 и
|
# Первую модель из /v1/models НЕ берём: там бывают не те модели (zai-glm-5.3 с автороутером
|
||||||
# картинки через серверный analyze_image. zai-glm-5.3 - OpenAI-эндпоинт с автороутером,
|
# во flash) и имена групп доступа (ZAI-Test-Group), которые не запустить. Если у ключа
|
||||||
# который подменяет модель на glm-5.3-flash.
|
# нет anth-glm-5.3 - выходим с понятной ошибкой вместо 400/403 внутри Claude Code.
|
||||||
model="${ELTEX_MODEL:-}"
|
model="${ELTEX_MODEL:-}"
|
||||||
if [ -z "$model" ]; then
|
if [ -z "$model" ]; then
|
||||||
model=$(echo "$_CLAUDE_TEST_BODY" | python3 -c '
|
if avail=$(echo "$_CLAUDE_TEST_BODY" | python3 -c '
|
||||||
import sys, json
|
import sys, json
|
||||||
ids = [m["id"] for m in json.load(sys.stdin).get("data", [])]
|
print(" ".join(m["id"] for m in json.load(sys.stdin)["data"]))' 2>/dev/null); then
|
||||||
print("anth-glm-5.3" if "anth-glm-5.3" in ids else (ids[0] if ids else ""))' 2>/dev/null)
|
case " $avail " in
|
||||||
|
*" anth-glm-5.3 "*) model="anth-glm-5.3" ;;
|
||||||
|
*)
|
||||||
|
echo -e "\033[0;31m[МОДЕЛЬ]\033[0m Ключу недоступна модель anth-glm-5.3. Прокси отдаёт: ${avail:-(пусто)}"
|
||||||
|
echo " Напиши админам про доступ к anth-glm-5.3 или задай модель явно: ELTEX_MODEL=<имя> ai-eltex"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
# Список моделей не получили (ошибка сервера) - пробуем модель по умолчанию
|
||||||
|
model="anth-glm-5.3"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
model="${model:-anth-glm-5.3}"
|
|
||||||
echo "Модель: $model"
|
echo "Модель: $model"
|
||||||
|
|
||||||
_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
|
_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
|
||||||
@@ -1792,9 +1806,17 @@ export AI_LAUNCHER=eltex
|
|||||||
export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/eltex"
|
export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/eltex"
|
||||||
# Пикер: все три слота = модель ключа. availableModels НЕ задаём (он схлопывает пикер);
|
# Пикер: все три слота = модель ключа. availableModels НЕ задаём (он схлопывает пикер);
|
||||||
# незаданный слот показал бы чужой Claude.
|
# незаданный слот показал бы чужой Claude.
|
||||||
# Effort: Claude Code шлёт output_config.effort и без CLAUDE_CODE_ALWAYS_ENABLE_EFFORT,
|
# Effort как у ai-glm: старт на max через CLI --effort (перебивает effortLevel из
|
||||||
# но LiteLLM прокси его вырезает, поэтому /effort пока ни на что не влияет (07.10:
|
# settings.json; AI_EFFORT=max в settings Claude Code не принимает и шлёт high), /effort
|
||||||
# напрямую в Z.ai low/max = 81/3712 выходных токенов, через прокси ~3-5K на любом уровне).
|
# в сессии работает. С 09.10 прокси пропускает output_config.effort для anth-glm-5.3:
|
||||||
|
# low/medium/high/max = 68/83/~4-6K/~4-5K выходных токенов (Z.ai: low,medium->low;
|
||||||
|
# high,xhigh->high; max). CLAUDE_CODE_ALWAYS_ENABLE_EFFORT - страховка, как в ai-glm.
|
||||||
|
# Имена-алиасы claude-* на прокси НЕ используем: claude-3-5-sonnet Claude Code считает
|
||||||
|
# старым Claude 3.5 Sonnet (без effort и thinking, max_tokens=8192); у claude-5-5-sonnet
|
||||||
|
# один деплоймент на всех, модели говорят, что она Claude Sonnet, а statusline показывает
|
||||||
|
# аккаунт Claude.ai. На качество имя не влияет (09.10, одинаковый запрос с разной строкой
|
||||||
|
# "You are powered by": разброс внутри одного имени больше разницы между именами).
|
||||||
|
# CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 вместо дефолтных 32000, как у ai-glm.
|
||||||
# API_TIMEOUT_MS из доки админов: прокси бывает медленным.
|
# API_TIMEOUT_MS из доки админов: прокси бывает медленным.
|
||||||
# Окно контекста 1M, как у ai-glm (та же glm-5.3 на Anthropic-эндпоинте Z.ai; через прокси
|
# Окно контекста 1M, как у ai-glm (та же glm-5.3 на Anthropic-эндпоинте Z.ai; через прокси
|
||||||
# 07.10 прошёл запрос на 598K входных токенов). Автокомпакт на 200K из доки админов и
|
# 07.10 прошёл запрос на 598K входных токенов). Автокомпакт на 200K из доки админов и
|
||||||
@@ -1804,8 +1826,17 @@ export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/eltex"
|
|||||||
# ENABLE_TOOL_SEARCH НЕ включаем: на anthropic-деплойментах прокси (Z.ai /api/anthropic)
|
# ENABLE_TOOL_SEARCH НЕ включаем: на anthropic-деплойментах прокси (Z.ai /api/anthropic)
|
||||||
# GLM с ним путается - ищет Read/Bash через ToolSearch и решает, что их нет. Цена -
|
# GLM с ним путается - ищет Read/Bash через ToolSearch и решает, что их нет. Цена -
|
||||||
# схемы встроенных тулов в каждом запросе (~+11K входных токенов).
|
# схемы встроенных тулов в каждом запросе (~+11K входных токенов).
|
||||||
_setup_isolated_config eltex opus high ''
|
_setup_isolated_config eltex opus xhigh ''
|
||||||
_apply_effort eltex high
|
# /logout-eltex: без зарегистрированной команды Claude Code отвечает "Unknown command" и
|
||||||
|
# не зовёт UserPromptSubmit-хук (logout-account-hook.sh), а встроенный /logout хук обходит.
|
||||||
|
# Заглушка-команда в конфиге лаунчера пропускает /logout-eltex до хука, он удаляет ключ.
|
||||||
|
mkdir -p "$CLAUDE_CONFIG_DIR/commands"
|
||||||
|
cat > "$CLAUDE_CONFIG_DIR/commands/logout-eltex.md" <<'CMDEOF'
|
||||||
|
---
|
||||||
|
description: Удалить сохранённый ключ ai-eltex (обрабатывает хук, без LLM)
|
||||||
|
---
|
||||||
|
Хук не сработал: удали ~/.config/ai-setup/eltex_key вручную и перезапусти ai-eltex (или запусти AI_RESET_KEY=1 ai-eltex).
|
||||||
|
CMDEOF
|
||||||
ANTHROPIC_BASE_URL="$eltex_base_url" \
|
ANTHROPIC_BASE_URL="$eltex_base_url" \
|
||||||
ANTHROPIC_AUTH_TOKEN="$api_key" \
|
ANTHROPIC_AUTH_TOKEN="$api_key" \
|
||||||
ANTHROPIC_DEFAULT_OPUS_MODEL="$model" \
|
ANTHROPIC_DEFAULT_OPUS_MODEL="$model" \
|
||||||
@@ -1819,12 +1850,151 @@ ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="$model" \
|
|||||||
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="$model - корпоративный прокси Eltex" \
|
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="$model - корпоративный прокси Eltex" \
|
||||||
CLAUDE_CODE_SUBAGENT_MODEL="$model" \
|
CLAUDE_CODE_SUBAGENT_MODEL="$model" \
|
||||||
CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000 \
|
CLAUDE_CODE_MAX_CONTEXT_TOKENS=1000000 \
|
||||||
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 \
|
||||||
|
CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1 \
|
||||||
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
||||||
API_TIMEOUT_MS=600000 \
|
API_TIMEOUT_MS=600000 \
|
||||||
claude --dangerously-skip-permissions --system-prompt-file "$_PROMPT_FILE" "$@"
|
claude --dangerously-skip-permissions --effort "${AI_EFFORT:-max}" --system-prompt-file "$_PROMPT_FILE" "$@"
|
||||||
ELTEXEOF
|
ELTEXEOF
|
||||||
chmod +x "$BIN_DIR/ai-eltex"
|
chmod +x "$BIN_DIR/ai-eltex"
|
||||||
|
|
||||||
|
# === ai-eltex-qwen ===
|
||||||
|
cat > "$BIN_DIR/ai-eltex-qwen" << 'ELTEXQWENEOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ai-eltex-qwen - запуск Claude Code через корпоративный LLM-прокси Eltex на моделях Qwen
|
||||||
|
# (Eltex-Coder-Senior = Qwen3.8-27B, Eltex-Qwen3_8-Flash-Next = Qwen3.8-Flash-Next-FP8).
|
||||||
|
# Ключ отдельный от ai-eltex, его выдают админы.
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/ai-api-helpers.sh" 2>/dev/null || true
|
||||||
|
|
||||||
|
key_file="$HOME/.config/ai-setup/eltex_qwen_key"
|
||||||
|
api_key=""
|
||||||
|
reauth=0
|
||||||
|
|
||||||
|
# Принудительный сброс сохранённого ключа через AI_RESET_KEY=1
|
||||||
|
if [ "${AI_RESET_KEY:-0}" == "1" ] && [ -f "$key_file" ]; then
|
||||||
|
rm -f "$key_file"
|
||||||
|
api_key=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -f "$key_file" ] && api_key=$(cat "$key_file")
|
||||||
|
# Ключ из онбординга Eltex AI (переменная для opencode), если своего файла ещё нет
|
||||||
|
if [ -z "$api_key" ] && [ "${AI_RESET_KEY:-0}" != "1" ]; then
|
||||||
|
api_key="${AI_ELTEX_LLM_API_KEY:-}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
eltex_base_url="${ELTEX_BASE_URL:-http://llm.eltex.loc:4000}"
|
||||||
|
|
||||||
|
# _eltex_check_key: проверка ключа через GET /v1/models (как в ai-eltex), заодно отдаёт
|
||||||
|
# модели, разрешённые ключу.
|
||||||
|
_eltex_check_key() {
|
||||||
|
local response
|
||||||
|
# Без "|| echo 000": при сетевой ошибке -w сам печатает 000.
|
||||||
|
response=$(curl -s -w "\n%{http_code}" --max-time 15 "$eltex_base_url/v1/models" \
|
||||||
|
-H "Authorization: Bearer $1" \
|
||||||
|
2>/dev/null)
|
||||||
|
_CLAUDE_TEST_CODE=$(echo "$response" | tail -1)
|
||||||
|
_CLAUDE_TEST_BODY=$(echo "$response" | sed '$d')
|
||||||
|
if [ "$_CLAUDE_TEST_CODE" = "000" ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "\033[0;31m[СЕТЬ]\033[0m Прокси $eltex_base_url недоступен."
|
||||||
|
echo " Нужна сеть Eltex. Если не резолвится llm.eltex.loc - перезапусти ru-bypass (добавит его в /etc/hosts)"
|
||||||
|
echo " или задай адрес явно: ELTEX_BASE_URL=http://172.16.4.68:4000 ai-eltex-qwen"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
_handle_openai_api_response "Eltex LLM" "$_CLAUDE_TEST_CODE" "$_CLAUDE_TEST_BODY" "Лимит исчерпан - обратись к админам"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$api_key" ]; then
|
||||||
|
echo -n "Проверка сохранённого Eltex Qwen ключа... "
|
||||||
|
_eltex_check_key "$api_key"
|
||||||
|
ret=$_API_RET
|
||||||
|
if [ $ret -eq 401 ]; then
|
||||||
|
rm -f "$key_file"
|
||||||
|
api_key=""
|
||||||
|
reauth=1
|
||||||
|
elif [ $ret -eq 429 ]; then
|
||||||
|
echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] "
|
||||||
|
read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) exit 1 ;; esac
|
||||||
|
elif [ $ret -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$api_key" ] && [ "$reauth" -eq 1 ]; then
|
||||||
|
echo -n "Хотите ввести новый Eltex Qwen ключ? [Y/n] "
|
||||||
|
read -r _ans; case "${_ans:-Y}" in [Yy]*) ;; *) exit 1 ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$api_key" ]; then
|
||||||
|
echo "Ключ выдают админы (ключ для Eltex-Coder-Senior, не GLM-ключ от ai-eltex)."
|
||||||
|
echo "Можно задать его в AI_ELTEX_LLM_API_KEY, как в онбординге Eltex AI."
|
||||||
|
read -r -p "Введите ваш Eltex Qwen ключ (sk-...): " api_key
|
||||||
|
[ -z "$api_key" ] && { echo "Выход."; exit 1; }
|
||||||
|
|
||||||
|
echo -n "Проверяю ключ... "
|
||||||
|
_eltex_check_key "$api_key"
|
||||||
|
ret=$_API_RET
|
||||||
|
if [ $ret -eq 0 ] || [ $ret -eq 429 ]; then
|
||||||
|
mkdir -p "$(dirname "$key_file")"
|
||||||
|
echo "$api_key" > "$key_file"
|
||||||
|
chmod 600 "$key_file"
|
||||||
|
echo "Ключ сохранён."
|
||||||
|
if [ $ret -eq 429 ]; then
|
||||||
|
echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] "
|
||||||
|
read -r _ans; case "${_ans:-N}" in [Yy]*) ;; *) exit 1 ;; esac
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Ключ НЕ сохранён."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Модель: ELTEX_QWEN_MODEL, иначе Eltex-Coder-Senior, если она есть у ключа, иначе
|
||||||
|
# Eltex-Qwen3_8-Flash-Next. Эмбеддер/реранкер из /v1/models для чата не годятся.
|
||||||
|
model="${ELTEX_QWEN_MODEL:-}"
|
||||||
|
if [ -z "$model" ]; then
|
||||||
|
model=$(echo "$_CLAUDE_TEST_BODY" | python3 -c '
|
||||||
|
import sys, json
|
||||||
|
ids = [m["id"] for m in json.load(sys.stdin).get("data", [])]
|
||||||
|
print(next((m for m in ("Eltex-Coder-Senior", "Eltex-Qwen3_8-Flash-Next") if m in ids), ""))' 2>/dev/null)
|
||||||
|
fi
|
||||||
|
model="${model:-Eltex-Coder-Senior}"
|
||||||
|
echo "Модель: $model"
|
||||||
|
|
||||||
|
_PROMPT_FILE=$(mktemp /tmp/ai-sys-prompt.XXXXXX)
|
||||||
|
trap 'rm -f "$_PROMPT_FILE"' EXIT INT TERM
|
||||||
|
_build_ai_sys_prompt > "$_PROMPT_FILE"
|
||||||
|
export AI_LAUNCHER=eltex-qwen
|
||||||
|
export CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/eltex-qwen"
|
||||||
|
# Пикер: все три слота = модель ключа (незаданный слот показал бы чужой Claude).
|
||||||
|
# Окно 200K: у Qwen3.8-Flash-Next max_input_tokens=204800. CLAUDE_CODE_DISABLE_1M_CONTEXT
|
||||||
|
# обязателен: без него Claude Code на чистом конфиге переводит opus в opus[1m] (окно 1M).
|
||||||
|
# Effort прокси игнорирует (08.10: low/high = 3227/3297, 09.10: low/max = 4424-5838/
|
||||||
|
# 977-3370 выходных токенов), /effort ни на что не влияет. Стартуем на max через CLI
|
||||||
|
# --effort, как ai-glm и ai-eltex. Картинки Qwen3.8-Flash-Next видит сама.
|
||||||
|
# max_output_tokens=8192 из /models прокси не применяет (09.10 принят max_tokens=64000),
|
||||||
|
# поэтому вывод поднят до 64000, как у ai-glm и ai-eltex.
|
||||||
|
_setup_isolated_config eltex-qwen opus high ''
|
||||||
|
ANTHROPIC_BASE_URL="$eltex_base_url" \
|
||||||
|
ANTHROPIC_AUTH_TOKEN="$api_key" \
|
||||||
|
ANTHROPIC_DEFAULT_OPUS_MODEL="$model" \
|
||||||
|
ANTHROPIC_DEFAULT_OPUS_MODEL_NAME="$model" \
|
||||||
|
ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION="$model - корпоративный прокси Eltex" \
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL="$model" \
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL_NAME="$model" \
|
||||||
|
ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION="$model - корпоративный прокси Eltex" \
|
||||||
|
ANTHROPIC_DEFAULT_HAIKU_MODEL="$model" \
|
||||||
|
ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="$model" \
|
||||||
|
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="$model - корпоративный прокси Eltex" \
|
||||||
|
CLAUDE_CODE_SUBAGENT_MODEL="$model" \
|
||||||
|
CLAUDE_CODE_DISABLE_1M_CONTEXT=1 \
|
||||||
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 \
|
||||||
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \
|
||||||
|
API_TIMEOUT_MS=600000 \
|
||||||
|
claude --dangerously-skip-permissions --effort "${AI_EFFORT:-max}" --system-prompt-file "$_PROMPT_FILE" "$@"
|
||||||
|
ELTEXQWENEOF
|
||||||
|
chmod +x "$BIN_DIR/ai-eltex-qwen"
|
||||||
|
|
||||||
# === ai-openrouter ===
|
# === ai-openrouter ===
|
||||||
cat > "$BIN_DIR/ai-openrouter" << 'OPENROUTEREOF'
|
cat > "$BIN_DIR/ai-openrouter" << 'OPENROUTEREOF'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
@@ -2126,7 +2296,8 @@ source "$BIN_DIR/ai-api-helpers.sh"
|
|||||||
_setup_isolated_config deepseek opus high ''
|
_setup_isolated_config deepseek opus high ''
|
||||||
_setup_isolated_config kimi opus high ''
|
_setup_isolated_config kimi opus high ''
|
||||||
_setup_isolated_config glm opus xhigh ''
|
_setup_isolated_config glm opus xhigh ''
|
||||||
_setup_isolated_config eltex opus high ''
|
_setup_isolated_config eltex opus xhigh ''
|
||||||
|
_setup_isolated_config eltex-qwen opus high ''
|
||||||
_setup_isolated_config openrouter "openai/gpt-5.5" high ''
|
_setup_isolated_config openrouter "openai/gpt-5.5" high ''
|
||||||
_setup_isolated_config opencode deepseek-v4-pro high ''
|
_setup_isolated_config opencode deepseek-v4-pro high ''
|
||||||
|
|
||||||
@@ -2137,7 +2308,7 @@ rm -f "$BIN_DIR/claude_gpt" "$BIN_DIR/claude_deepseek" "$BIN_DIR/claude_kimi" "$
|
|||||||
if [ "$BIN_DIR" != "$HOME/.local/bin" ]; then
|
if [ "$BIN_DIR" != "$HOME/.local/bin" ]; then
|
||||||
warn "BIN_DIR=$BIN_DIR — удаляю старые скрипты из ~/.local/bin/ ..."
|
warn "BIN_DIR=$BIN_DIR — удаляю старые скрипты из ~/.local/bin/ ..."
|
||||||
rm -f "$HOME/.local/bin/ai-gpt" "$HOME/.local/bin/ai-deepseek" "$HOME/.local/bin/ai-kimi" \
|
rm -f "$HOME/.local/bin/ai-gpt" "$HOME/.local/bin/ai-deepseek" "$HOME/.local/bin/ai-kimi" \
|
||||||
"$HOME/.local/bin/ai-glm" "$HOME/.local/bin/ai-eltex" "$HOME/.local/bin/ai-openrouter" "$HOME/.local/bin/ai-gemini" "$HOME/.local/bin/ai-cursor" "$HOME/.local/bin/ai-claude" \
|
"$HOME/.local/bin/ai-glm" "$HOME/.local/bin/ai-eltex" "$HOME/.local/bin/ai-eltex-qwen" "$HOME/.local/bin/ai-openrouter" "$HOME/.local/bin/ai-gemini" "$HOME/.local/bin/ai-cursor" "$HOME/.local/bin/ai-claude" \
|
||||||
"$HOME/.local/bin/ai-opencode" \
|
"$HOME/.local/bin/ai-opencode" \
|
||||||
"$HOME/.local/bin/ai-api-helpers.sh" "$HOME/.local/bin/claude-gpt-effort-proxy.py"
|
"$HOME/.local/bin/ai-api-helpers.sh" "$HOME/.local/bin/claude-gpt-effort-proxy.py"
|
||||||
fi
|
fi
|
||||||
@@ -2158,6 +2329,7 @@ echo -e " ${CYAN}ai-deepseek${NC} - DeepSeek (через Claude Code,
|
|||||||
echo -e " ${CYAN}ai-kimi${NC} - Kimi K2.7 Code (через Claude Code, API ключ сохраняется)"
|
echo -e " ${CYAN}ai-kimi${NC} - Kimi K2.7 Code (через Claude Code, API ключ сохраняется)"
|
||||||
echo -e " ${CYAN}ai-glm${NC} - GLM 5.3 (через Claude Code, Zhipu AI API)"
|
echo -e " ${CYAN}ai-glm${NC} - GLM 5.3 (через Claude Code, Zhipu AI API)"
|
||||||
echo -e " ${CYAN}ai-eltex${NC} - Корпоративный LLM-прокси Eltex (через Claude Code, ключ от админов)"
|
echo -e " ${CYAN}ai-eltex${NC} - Корпоративный LLM-прокси Eltex (через Claude Code, ключ от админов)"
|
||||||
|
echo -e " ${CYAN}ai-eltex-qwen${NC} - Корпоративный LLM-прокси Eltex, модели Qwen (через Claude Code, свой ключ)"
|
||||||
echo -e " ${CYAN}ai-openrouter${NC} - OpenRouter (через Claude Code: GPT-5.5, Grok, Qwen, GLM 5.2 и др.)"
|
echo -e " ${CYAN}ai-openrouter${NC} - OpenRouter (через Claude Code: GPT-5.5, Grok, Qwen, GLM 5.2 и др.)"
|
||||||
echo -e " ${CYAN}ai-opencode${NC} - OpenCode Go (через Claude Code + routatic-proxy: DeepSeek, Kimi, Qwen, GLM, MiniMax, MiMo)"
|
echo -e " ${CYAN}ai-opencode${NC} - OpenCode Go (через Claude Code + routatic-proxy: DeepSeek, Kimi, Qwen, GLM, MiniMax, MiMo)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -245,7 +245,8 @@ if [ -n "${ALL_EXC// }" ]; then
|
|||||||
if echo "$item" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$"; then
|
if echo "$item" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$"; then
|
||||||
ips="$item"
|
ips="$item"
|
||||||
else
|
else
|
||||||
ips=$(getent hosts "$item" 2>/dev/null | awk '{print $1}' | sort -u)
|
ips=$(getent ahostsv4 "$item" 2>/dev/null | awk '{print $1}' | sort -u)
|
||||||
|
[ -z "$ips" ] && ips=$(getent hosts "$item" 2>/dev/null | awk '{print $1}' | sort -u)
|
||||||
fi
|
fi
|
||||||
for ip in $ips; do
|
for ip in $ips; do
|
||||||
ipset add ru-direct "$ip" -exist 2>/dev/null || true
|
ipset add ru-direct "$ip" -exist 2>/dev/null || true
|
||||||
@@ -299,7 +300,8 @@ if [ -n "${ALL_EXC// }" ]; then
|
|||||||
if echo "$item" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$"; then
|
if echo "$item" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$"; then
|
||||||
ips="$item"
|
ips="$item"
|
||||||
else
|
else
|
||||||
ips=$(getent hosts "$item" 2>/dev/null | awk '{print $1}' | sort -u)
|
ips=$(getent ahostsv4 "$item" 2>/dev/null | awk '{print $1}' | sort -u)
|
||||||
|
[ -z "$ips" ] && ips=$(getent hosts "$item" 2>/dev/null | awk '{print $1}' | sort -u)
|
||||||
fi
|
fi
|
||||||
for ip in $ips; do
|
for ip in $ips; do
|
||||||
ip route replace "$ip/32" via "$GATEWAY" dev "$DEV" 2>/dev/null
|
ip route replace "$ip/32" via "$GATEWAY" dev "$DEV" 2>/dev/null
|
||||||
|
|||||||
4
setup.sh
4
setup.sh
@@ -230,7 +230,7 @@ case "$choice" in
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${BLD}AI инструменты:${CLR}"
|
echo -e "${BLD}AI инструменты:${CLR}"
|
||||||
for cmd in ai-claude ai-gpt ai-deepseek ai-kimi ai-glm ai-eltex ai-openrouter ai-opencode ai-gemini android; do
|
for cmd in ai-claude ai-gpt ai-deepseek ai-kimi ai-glm ai-eltex ai-eltex-qwen ai-openrouter ai-opencode ai-gemini android; do
|
||||||
if command -v "$cmd" &>/dev/null; then
|
if command -v "$cmd" &>/dev/null; then
|
||||||
echo -e " ${GRN}✓${CLR} $cmd"
|
echo -e " ${GRN}✓${CLR} $cmd"
|
||||||
else
|
else
|
||||||
@@ -241,7 +241,7 @@ case "$choice" in
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e "${BLD}API ключи:${CLR}"
|
echo -e "${BLD}API ключи:${CLR}"
|
||||||
cfg="$HOME/.config/ai-setup"
|
cfg="$HOME/.config/ai-setup"
|
||||||
for f in deepseek_key kimi_key glm_key eltex_key openrouter_key opencode_key; do
|
for f in deepseek_key kimi_key glm_key eltex_key eltex_qwen_key openrouter_key opencode_key; do
|
||||||
name="${f/_key/}"
|
name="${f/_key/}"
|
||||||
if [ -s "$cfg/$f" ]; then
|
if [ -s "$cfg/$f" ]; then
|
||||||
echo -e " ${GRN}✓${CLR} $name"
|
echo -e " ${GRN}✓${CLR} $name"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ GPT_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gpt"/,/^GPTEOF/' "$SCRIPT")
|
|||||||
KIMI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-kimi"/,/^KIMIEOF/' "$SCRIPT")
|
KIMI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-kimi"/,/^KIMIEOF/' "$SCRIPT")
|
||||||
GLM_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-glm"/,/^GLMEOF/' "$SCRIPT")
|
GLM_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-glm"/,/^GLMEOF/' "$SCRIPT")
|
||||||
ELTEX_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-eltex"/,/^ELTEXEOF/' "$SCRIPT")
|
ELTEX_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-eltex"/,/^ELTEXEOF/' "$SCRIPT")
|
||||||
|
ELTEX_QWEN_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-eltex-qwen"/,/^ELTEXQWENEOF/' "$SCRIPT")
|
||||||
LOGOUT_HOOK="$(cd "$(dirname "$0")/.." && pwd)/home-configs/claude/hooks/logout-account-hook.sh"
|
LOGOUT_HOOK="$(cd "$(dirname "$0")/.." && pwd)/home-configs/claude/hooks/logout-account-hook.sh"
|
||||||
GEMINI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gemini"/,/^GEMINIEOF/' "$SCRIPT")
|
GEMINI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gemini"/,/^GEMINIEOF/' "$SCRIPT")
|
||||||
OPENCODE_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-opencode"/,/^OPENCODEEOF/' "$SCRIPT")
|
OPENCODE_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-opencode"/,/^OPENCODEEOF/' "$SCRIPT")
|
||||||
@@ -123,12 +124,13 @@ test_eltex_claude_launcher() {
|
|||||||
test_eltex_model_from_key() {
|
test_eltex_model_from_key() {
|
||||||
if echo "$ELTEX_SECTION" | grep -q '/v1/models' \
|
if echo "$ELTEX_SECTION" | grep -q '/v1/models' \
|
||||||
&& echo "$ELTEX_SECTION" | grep -q 'model="${ELTEX_MODEL:-}"' \
|
&& echo "$ELTEX_SECTION" | grep -q 'model="${ELTEX_MODEL:-}"' \
|
||||||
&& echo "$ELTEX_SECTION" | grep -q '"anth-glm-5.3" if "anth-glm-5.3" in ids' \
|
&& echo "$ELTEX_SECTION" | grep -q '\*" anth-glm-5.3 "\*) model="anth-glm-5.3"' \
|
||||||
|
&& ! echo "$ELTEX_SECTION" | grep -q 'ids\[0\]' \
|
||||||
&& echo "$ELTEX_SECTION" | grep -q 'ANTHROPIC_DEFAULT_OPUS_MODEL="$model"' \
|
&& echo "$ELTEX_SECTION" | grep -q 'ANTHROPIC_DEFAULT_OPUS_MODEL="$model"' \
|
||||||
&& echo "$ELTEX_SECTION" | grep -q 'ANTHROPIC_DEFAULT_HAIKU_MODEL="$model"'; then
|
&& echo "$ELTEX_SECTION" | grep -q 'ANTHROPIC_DEFAULT_HAIKU_MODEL="$model"'; then
|
||||||
ok "ai-eltex: prefers anth-glm-5.3 from the key's /v1/models (ELTEX_MODEL overrides)"
|
ok "ai-eltex: uses anth-glm-5.3, never the first /v1/models entry (ELTEX_MODEL overrides)"
|
||||||
else
|
else
|
||||||
fail "ai-eltex: must prefer anth-glm-5.3 from /v1/models, ELTEX_MODEL overrides"
|
fail "ai-eltex: must use anth-glm-5.3 (not the first /v1/models entry), ELTEX_MODEL overrides"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +154,67 @@ test_eltex_context_window() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── ai-eltex-qwen: свой ключ и конфиг, модели Qwen, окно 200K ─────────────
|
||||||
|
test_eltex_qwen_launcher() {
|
||||||
|
if echo "$ELTEX_QWEN_SECTION" | grep -q 'claude --dangerously-skip-permissions' \
|
||||||
|
&& echo "$ELTEX_QWEN_SECTION" | grep -q 'key_file="$HOME/.config/ai-setup/eltex_qwen_key"' \
|
||||||
|
&& echo "$ELTEX_QWEN_SECTION" | grep -q 'CLAUDE_CONFIG_DIR="$HOME/.config/ai-setup/cfg/eltex-qwen"' \
|
||||||
|
&& echo "$ELTEX_QWEN_SECTION" | grep -q 'model="${ELTEX_QWEN_MODEL:-}"' \
|
||||||
|
&& echo "$ELTEX_QWEN_SECTION" | grep -q '("Eltex-Coder-Senior", "Eltex-Qwen3_8-Flash-Next")' \
|
||||||
|
&& echo "$ELTEX_QWEN_SECTION" | grep -q 'api_key="${AI_ELTEX_LLM_API_KEY:-}"' \
|
||||||
|
&& echo "$ELTEX_QWEN_SECTION" | grep -q '^CLAUDE_CODE_DISABLE_1M_CONTEXT=1'; then
|
||||||
|
ok "ai-eltex-qwen: own key/config, Coder-Senior then Flash-Next, 200K window"
|
||||||
|
else
|
||||||
|
fail "ai-eltex-qwen: must use eltex_qwen_key, cfg/eltex-qwen, Qwen model preference and DISABLE_1M"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_logout_eltex_qwen() {
|
||||||
|
local tmp_home rc=0
|
||||||
|
tmp_home=$(mktemp -d)
|
||||||
|
mkdir -p "$tmp_home/.config/ai-setup"
|
||||||
|
echo "sk-test" > "$tmp_home/.config/ai-setup/eltex_qwen_key"
|
||||||
|
echo "sk-glm" > "$tmp_home/.config/ai-setup/eltex_key"
|
||||||
|
echo '{"prompt":"/logout"}' | HOME="$tmp_home" AI_LAUNCHER=eltex-qwen bash "$LOGOUT_HOOK" 2>/dev/null || rc=$?
|
||||||
|
if [ "$rc" -eq 2 ] && [ ! -f "$tmp_home/.config/ai-setup/eltex_qwen_key" ] && [ -f "$tmp_home/.config/ai-setup/eltex_key" ]; then
|
||||||
|
ok "logout hook: /logout in ai-eltex-qwen removes only eltex_qwen_key"
|
||||||
|
else
|
||||||
|
fail "logout hook: /logout in ai-eltex-qwen must remove eltex_qwen_key and keep eltex_key (rc=$rc)"
|
||||||
|
fi
|
||||||
|
rm -rf "$tmp_home"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── ai-eltex / ai-eltex-qwen: effort max по умолчанию через CLI, вывод 64000 ────
|
||||||
|
test_eltex_effort() {
|
||||||
|
if echo "$ELTEX_SECTION" | grep -q -- '--effort "${AI_EFFORT:-max}"' \
|
||||||
|
&& echo "$ELTEX_SECTION" | grep -q '^CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1' \
|
||||||
|
&& echo "$ELTEX_SECTION" | grep -q '^CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000' \
|
||||||
|
&& ! echo "$ELTEX_SECTION" | grep -q '^_apply_effort'; then
|
||||||
|
ok "ai-eltex: starts with --effort max by default, AI_EFFORT via CLI, 64000 output tokens"
|
||||||
|
else
|
||||||
|
fail "ai-eltex: must start with --effort \"\${AI_EFFORT:-max}\", CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1, MAX_OUTPUT_TOKENS=64000, no _apply_effort"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_eltex_qwen_effort() {
|
||||||
|
if echo "$ELTEX_QWEN_SECTION" | grep -q -- '--effort "${AI_EFFORT:-max}"' \
|
||||||
|
&& echo "$ELTEX_QWEN_SECTION" | grep -q '^CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000' \
|
||||||
|
&& ! echo "$ELTEX_QWEN_SECTION" | grep -q '^_apply_effort'; then
|
||||||
|
ok "ai-eltex-qwen: starts with --effort max by default, 64000 output tokens"
|
||||||
|
else
|
||||||
|
fail "ai-eltex-qwen: must start with --effort \"\${AI_EFFORT:-max}\" and MAX_OUTPUT_TOKENS=64000, no _apply_effort"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── ai-eltex: команда /logout-eltex зарегистрирована (иначе Unknown command, хук не зовётся) ──
|
||||||
|
test_eltex_logout_command() {
|
||||||
|
if echo "$ELTEX_SECTION" | grep -q 'cat > "$CLAUDE_CONFIG_DIR/commands/logout-eltex.md"'; then
|
||||||
|
ok "ai-eltex: registers /logout-eltex command so the logout hook receives it"
|
||||||
|
else
|
||||||
|
fail "ai-eltex: must create \$CLAUDE_CONFIG_DIR/commands/logout-eltex.md"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# ── logout hook: /logout-eltex удаляет eltex_key ──────────────────────────
|
# ── logout hook: /logout-eltex удаляет eltex_key ──────────────────────────
|
||||||
test_logout_eltex() {
|
test_logout_eltex() {
|
||||||
local tmp_home rc=0
|
local tmp_home rc=0
|
||||||
@@ -167,13 +230,14 @@ test_logout_eltex() {
|
|||||||
rm -rf "$tmp_home"
|
rm -rf "$tmp_home"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── ai-glm: default effort is xhigh (still overridable via AI_EFFORT) ─────
|
# ── ai-glm: default effort is max (still overridable via AI_EFFORT) ───────
|
||||||
test_glm_default_effort() {
|
test_glm_default_effort() {
|
||||||
if echo "$GLM_SECTION" | grep -q -- '--effort "${AI_EFFORT:-xhigh}"' \
|
if echo "$GLM_SECTION" | grep -q -- '--effort "${AI_EFFORT:-max}"' \
|
||||||
|
&& echo "$GLM_SECTION" | grep -q '^CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000' \
|
||||||
&& echo "$GLM_SECTION" | grep -q 'CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1'; then
|
&& echo "$GLM_SECTION" | grep -q 'CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1'; then
|
||||||
ok "ai-glm: starts with --effort xhigh by default, effort sent to Z.ai"
|
ok "ai-glm: starts with --effort max by default, effort sent to Z.ai, 64000 output tokens"
|
||||||
else
|
else
|
||||||
fail "ai-glm: must start with --effort \"\${AI_EFFORT:-xhigh}\" and CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1"
|
fail "ai-glm: must start with --effort \"\${AI_EFFORT:-max}\", CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1 and MAX_OUTPUT_TOKENS=64000"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,7 +443,12 @@ test_eltex_claude_launcher
|
|||||||
test_eltex_model_from_key
|
test_eltex_model_from_key
|
||||||
test_eltex_no_tool_search
|
test_eltex_no_tool_search
|
||||||
test_eltex_context_window
|
test_eltex_context_window
|
||||||
|
test_eltex_logout_command
|
||||||
|
test_eltex_effort
|
||||||
|
test_eltex_qwen_effort
|
||||||
test_logout_eltex
|
test_logout_eltex
|
||||||
|
test_eltex_qwen_launcher
|
||||||
|
test_logout_eltex_qwen
|
||||||
test_gemini_native_launcher
|
test_gemini_native_launcher
|
||||||
test_cursor_native_launcher
|
test_cursor_native_launcher
|
||||||
test_global_rules_source_installed
|
test_global_rules_source_installed
|
||||||
|
|||||||
Reference in New Issue
Block a user