dsh-llm-gigachat
Verifieddsh-llm-gigachat · v0.1.1 · MIT
DeepSeek Harness (dsh) plugin that connects Sber GigaChat models: a built-in local OAuth proxy (client-credentials -> OpenAI-compatible endpoint, upstream serialization, legacy tool-calling translation) plus automatic provider-route bootstrap, so GigaChat
Install
dsh plugin add dsh-llm-gigachat Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-llm-gigachat
English version: README.md
Плагин DeepSeek Harness (dsh), который подключает модели Сбера GigaChat к harness «по-нормальному»: встроенный OAuth2-прокси + автоматическая настройка провайдера, который появляется на стандартной странице Settings → Models и выбирается обычным пикером моделей.
Без плагина GigaChat подключить «простой настройкой» нельзя: Sber использует OAuth 2.0 Client Credentials (двухшаговый обмен ключа на токен), а встроенные OpenAI-совместимые шлюзы harness такого не умеют — нужен код. Этот плагин содержит этот код (тот самый, что проверен в gigachat-proxy.mjs): обмен ключа на токен, кэш токена на 30 минут, сериализация запросов (личный тариф ≈ 1 одновременный запрос, иначе 429), прозрачный ретрай вырожденных ответов "<" и трансляция tool-calling в устаревший формат functions/function_call, который понимает GigaChat 3.
⚠️ Важно про модели: агентский чат с инструментами надёжно тянут только
GigaChat-3-UltraиGigaChat-3-Pro. Младшие модели (GigaChat-3-Lightning,GigaChat-2*) отвергают сложные агентские схемы (422: Field 'properties.args.properties' is missing— прокси это чинит санитизацией) или галлюцинируют вызовы инструментов с невалидными аргументами — это уже нефиксируемый предел самой модели. Поэтому плагин по умолчанию пускает младшие модели без инструментов (текстовый режим,stripToolsFor), а Ultra/Pro — в полном агентском режиме. Хотите дать инструментам слабой модели — уберите её изstripToolsFor.
Содержание
- Инструкция установки
- Как добавить провайдера Sber и какой ключ куда вставлять
- OAuth2: что происходит под капотом
- Конфигурация
- Откат / удаление плагина
- Troubleshooting
Установка
Вариант А. Из npm (рекомендуется)
dsh plugin --profile web add dsh-llm-gigachat
Устанавливает npm-пакет в профиль web. Затем добавьте плагин в список бандлов профиля ~/.dsh/profiles/web/package.json → dsh.profile.bundles:
"dsh": {
"profile": {
"bundles": [
// ... существующие ...
"dsh-llm-gigachat"
]
}
}
Перезапустите dsh web. Ручной эквивалент первого шага:
cd $env:USERPROFILE\.dsh\profiles\web
pnpm add dsh-llm-gigachat
Вариант Б. Из GitHub-репозитория (исходники, последний master)
dsh plugin --profile web add git+https://github.com/igrock88/dsh-llm-gigachat.git
Как это работает: pnpm (вызывается форвардером dsh plugin) клонирует репозиторий, собирает пакет из исходников и устанавливает его в node_modules профиля — удобно, когда нужен самый свежий master.
Затем добавьте плагин в список бандлов профиля ~/.dsh/profiles/web/package.json → dsh.profile.bundles:
"dsh": {
"profile": {
"bundles": [
// ... существующие ...
"dsh-llm-gigachat"
]
}
}
Перезапустите dsh web.
Вариант В. Локальная разработка (file:)
$profile = "$env:USERPROFILE\.dsh\profiles\web"
# 1. скопировать исходники
Copy-Item -Recurse -Force ".\dsh-llm-gigachat" "$profile\plugins\dsh-llm-gigachat"
# 2. в package.json профиля добавить зависимость:
# "dsh-llm-gigachat": "file:./plugins/dsh-llm-gigachat"
# и "dsh-llm-gigachat" в dsh.profile.bundles
# 3. установить зависимости и перезапустить
Push-Location $profile
pnpm install
Pop-Location
# перезапустить dsh web
Проверка БЕЗ запуска сервера (обязательно, безопасно)
Команда --dump-config не стартует dsh — только печатает собранное дерево конфигурации. В выводе должна появиться строка id: llm-gigachat, а конфиг llm-pi-ai должен остаться нетронутым:
dsh --profile web --dump-config
⚠️ После установки dsh должен «взлететь» сразу. Если нет — см. раздел Откат.
Как подключить провайдера Sber GigaChat (пошагово)
Шаг 1. Получите ключ OAuth2 в кабинете Sber
- Зайдите в кабинет разработчика: developers.sber.ru → GigaChat API (или Sber Studio → раздел GigaChat).
- Создайте приложение / подключите API. Кабинет выдаст два значения:
client_idиclient_secret;- либо сразу готовый «Ключ авторизации» / Authorization Key — это и есть та строка, что нам нужна.
- Уточните тип доступа, он задаёт
scope:- физлицо →
GIGACHAT_API_PERS(по умолчанию в плагине); - организация →
GIGACHAT_API_B2BилиGIGACHAT_API_CORP.
- физлицо →
Ключ авторизации — это не сам ключ API, а строка base64(client_id:client_secret) (два поля, склеенные двоеточием и закодированные в base64). Если кабинет даёт только раздельные client_id/client_secret, соберите её сами:
# PowerShell: base64("client_id:client_secret") — пример сборки
$pair = "client_id:client_secret"
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($pair))
Write-Host "Вставьте полученную строку как API key"
# или в bash:
printf 'client_id:client_secret' | base64
Пример вида строки:
MTIzNDU2Nzg5MDEyMzQ1Njc4OjE2OjE3OjE(у вас будет своя).
Шаг 2. Подключите провайдера и вставьте ключ
Два сценария — выберите свой.
Вариант A. Плагин установлен (рекомендуется)
Строка Sber GigaChat уже есть на странице Settings → Models — её создаёт плагин, и в ней уже заполнены endpoint http://127.0.0.1:8787/v1, протокол openai-completions и список моделей. Остаётся только ключ:
- Запустите
dsh web. - Откройте Settings → Models.
- Нажмите Edit на строке Sber GigaChat.
- В поле API key вставьте ключ из шага 1 (значение
base64(client_id:client_secret)). - Нажмите Apply.
⚠️ Попытка создать через «Add a custom provider» ещё одного провайдера с id
sberбудет отклонена («идентификатор уже занят») — и это правильно: провайдер уже подключён плагином. Просто используйте существующую строку.
Вариант B. Без плагина (внешний gigachat-proxy.mjs + стандартный интерфейс)
Если плагин не установлен, но внешний прокси запущен (порт 8787), подключите провайдера через стандартную карточку:
- Settings → Models → + Add a custom provider.
- Provider ID:
sber - Display name:
Sber GigaChat(или любое). - Base URL:
http://127.0.0.1:8787/v1— именно локальный прокси, неhttps://api.giga.chat/v1/(напрямую нельзя: pi-ai не делает OAuth2-обмен для hand-declared роутов — будет401). - API protocol:
openai-completions. - API key: ключ из шага 1.
- Models: нажмите Fetch available models — прокси отдаст список (
GigaChat-3-Ultra,GigaChat-3-Pro); либо Add model и введите id вручную (хотя бы одну модель — без неё карточка не сохранится). - Create provider.
При этом держите gigachat-proxy.mjs запущенным — без него endpoint мёртв.
Что произойдёт внутри в обоих вариантах: ключ сохранится только в управляемом хранилище ~/.dsh/.credentials.yaml (под именем SBER_API_KEY), а в настройки провайдера запишется ссылка apiKeyEnv: SBER_API_KEY — само значение в settings.yaml не попадёт.
Альтернатива без GUI (эквивалент):
# ~/.dsh/.credentials.yaml
refs:
SBER_API_KEY: "<base64(client_id:client_secret)>"
или переменная окружения перед запуском dsh:
set SBER_API_KEY=<base64(client_id:client_secret)>
dsh web
Шаг 3. Выберите модель и проверьте
- В пикере модели (шапка чата) или в Settings → Models выберите: провайдер sber, модель
GigaChat-3-Ultra(илиGigaChat-3-Pro). - Отправьте сообщение. Должен прийти ответ модели.
Проверка «живости» прокси (плагин поднимает его на 127.0.0.1:8787) и счётчики:
Invoke-WebRequest -Uri "http://127.0.0.1:8787/v1/models" -UseBasicParsing
Invoke-WebRequest -Uri "http://127.0.0.1:8787/stats" -UseBasicParsing
# stats: { served, rateLimited, serverErrors, degenerateRetries, toolCallsTranslated, queueDepth }
Если на порту 8787 уже запущен внешний
gigachat-proxy.mjs— плагин обнаружит его (/v1/modelsотвечает) и не будет поднимать второй сервер, а просто переиспользует существующий. Остановите внешний скрипт, чтобы прокси жил внутри harness.
OAuth2: коротко
GigaChat не принимает статический ключ напрямую. Каждый запрос выглядит так:
1) POST https://ngw.devices.sberbank.ru:9443/api/v2/oauth
Authorization: Basic <base64(client_id:client_secret)> ← ваш ключ из шага 1
RqUID: <uuid4> ← свежий на каждый запрос
body: scope=GIGACHAT_API_PERS
→ { access_token, expires_in: 1800 } ← живёт 30 минут
2) POST https://api.giga.chat/v1/chat/completions
Authorization: Bearer <access_token>
body: стандартный OpenAI JSON (stream / tools / …)
Плагин делает оба шага автоматически: достаёт ваш ключ из SBER_API_KEY, при первом запросе получает токен, кэширует его до истечения (минус запас), при 401 обновляет токен на лету. Плюс к этому:
- Сериализация запросов к api.giga.chat — личный тариф допускает ≈1 одновременный запрос, иначе
429(отсюда был «шквал 429» при параллельных запросах раньше). Очередь FIFO,maxConcurrencyпо умолчанию 1. - Защита от
"<"— вырожденный ответ ровно в один символ<прозрачно повторяется до 2 раз. - Tool calling — GigaChat 3 игнорирует современный
tools/tool_choice, но понимает легасиfunctions/function_call(аргументы — объектом). Прокси транслирует запрос и ответ в обе стороны, а результат функции (не-JSON текст) оборачивает в JSON-строку (иначе 422/500). - TLS — сертификаты НУЦ Минцифры не лежат в системном хранилище Node по умолчанию, поэтому проверка отключена (
tls.rejectUnauthorized: false). Для усиления: установите корневой сертификат НУЦ и включите проверку.
Конфигурация
Все настройки — секция gigachat: в ~/.dsh/settings.yaml (хот-релоад, без перезапуска):
gigachat:
enabled: true
host: 127.0.0.1
port: 8787 # порт прокси; на него указывает роут sber
upstreamBaseURL: https://api.giga.chat/v1
oauthURL: https://ngw.devices.sberbank.ru:9443/api/v2/oauth
scope: GIGACHAT_API_PERS # GIGACHAT_API_B2B / GIGACHAT_API_CORP для организаций
apiKeyEnv: SBER_API_KEY # ссылка на креденшал (ключ base64)
providerId: sber
displayName: Sber GigaChat
maxConcurrency: 1
tls:
rejectUnauthorized: false
models:
- id: GigaChat-3-Ultra
name: GigaChat 3 Ultra
- id: GigaChat-3-Pro
name: GigaChat 3 Pro
| Поле | По умолчанию | Смысл |
|---|---|---|
enabled |
true |
поднимать встроенный прокси |
host / port |
127.0.0.1 / 8787 |
адрес прокси; на него должен указывать роут |
upstreamBaseURL |
https://api.giga.chat/v1 |
базовый URL chat-completions |
oauthURL |
https://ngw.devices.sberbank.ru:9443/api/v2/oauth |
первый эндпоинт токена (легаси) |
scope |
GIGACHAT_API_PERS |
тип доступа (физлицо / организация) |
apiKeyEnv |
SBER_API_KEY |
имя ссылки на креденшал |
providerId |
sber |
id роута llm-pi-ai.providers.* и строки на странице Models |
displayName |
Sber GigaChat |
подпись в селекторах |
maxConcurrency |
1 |
одновременных upstream-запросов (личный тариф ~1) |
tls.rejectUnauthorized |
false |
проверять TLS (нужен корневой сертификат НУЦ) |
stripToolsFor |
GigaChat-3-Lightning, GigaChat-2-Max, GigaChat-2-Pro, GigaChat-2 |
эти модели по умолчанию отвечают без инструментов (текстовый режим): они отвергают сложные агентские схемы или галлюцинируют вызовы. Уберите модель из списка, чтобы дать ей инструменты; пустой список = инструменты у всех |
models |
6 chat-моделей GigaChat 2/3 | каталог по умолчанию; GET /v1/models при этом отдаёт живой список от Sber (только chat-модели, embedders отфильтрованы), а при недоступности API — этот настроенный список |
Особенности поведения:
Санитизация схем инструментов: прокси рекурсивно дополняет
properties: {}всем объектам в схемах функций — младшие модели (Lightning, GigaChat-2*) иначе отвечают422: Field 'properties.args.properties' is missing. Ultra/Pro терпеливы, но после санитизации работают все.Рекомендации по моделям: для агентских чатов (с инструментами) используйте
GigaChat-3-Ultra/GigaChat-3-Pro— младшие слабо следуют схемам и могут вызывать инструменты невпопад; поэтому по умолчанию для них включёнstripToolsFor(текстовый режим). Если модели из списка всё же нужны инструменты — уберите её изstripToolsForв секцииgigachat:.Если роут
sberвllm-pi-ai.providersуже существует (например, от старого standalone-прокси), плагин его не перезаписывает; единственное исключение — базаbaseURLперенаправляется на локальный прокси, если сейчас она указывает на127.0.0.1с другого порта.Если порт занят и отвечает списком моделей — считаем, что работает внешний прокси, второй сервер не поднимаем.
Роут создаётся через
settings.mutatepath-операциями — конфиг остальных провайдеров (openrouter,local, …) никогда не затрагивается.
Откат / удаление плагина
Главное правило: dsh перестаёт запускаться после установки плагина почти всегда из-за поломки манифеста
package.jsonпрофиля или чужихconfig:-патчей, а не из-за harness. Наш плагин никогда не патчит чужие строки, поэтому откат тривиален.
Штатное удаление (dsh работает)
cd $env:USERPROFILE\.dsh\profiles\web
pnpm remove dsh-llm-gigachat
или, если ставили через CLI:
dsh plugin --profile web remove dsh-llm-gigachat
Затем удалите "dsh-llm-gigachat" из dsh.profile.bundles в package.json профиля и перезапустите dsh web.
Аварийный откат (dsh НЕ запускается)
- Любым редактором откройте
~/.dsh/profiles/web/package.json:- удалите строку
"dsh-llm-gigachat": ...изdependencies; - удалите
"dsh-llm-gigachat"изdsh.profile.bundles.
- удалите строку
- Если вы вручную добавляли insert в
~/.dsh/profiles/web/cordis.patch.yml— удалите блокid: llm-gigachat. - Переустановите зависимости и проверьте дерево без запуска dsh:
cd $env:USERPROFILE\.dsh\profiles\web
pnpm install
dsh --profile web --dump-config # в выводе не должно быть llm-gigachat
- Запустите dsh снова. Если профиль всё ещё не грузится даже без плагина — ищите в
cordis.patch.ymlстроки вида- id: <чужой> config:(движок патчей заменяет конфиг целиком, не мержит — это и ломает конфиги вродеllm-pi-ai).
Очистка данных плагина (опционально)
# ~/.dsh/settings.yaml — удалить, если плагин больше не нужен
gigachat: # удалить
# llm-pi-ai.providers.sber удалять ТОЛЬКО если роут создан этим плагином
# (у более ранних установок роут sber мог быть и вручную — тогда оставьте)
Запись SBER_API_KEY в ~/.dsh/.credentials.yaml безвредна; удалите её, если ничто другое её не использует.
Troubleshooting
| Симптом | Причина / решение |
|---|---|
502 {"error": "...no GigaChat credentials..."} |
Вставьте ключ на странице Models (или задайте SBER_API_KEY в .credentials.yaml/окружении) — см. шаг 1–2 выше |
401 при запросах |
Токен протух между кэшем и запросом — плагин обновляет сам и повторяет; если повторяется постоянно — проверьте, что ключ действительно base64(client_id:client_secret) от того же приложения и с нужным scope |
Шквал 429 |
Личный тариф ≈1 одновременный запрос. Убедитесь, что включена сериализация (maxConcurrency: 1) и что нет второго внешнего прокси, конкурирующего с плагином |
Модель «отвечает» одним символом < |
Вырожденный ответ GigaChat под конкурентной нагрузкой — плагин повторяет прозрачно; счётчик degenerateRetries в /stats |
| Tool-calling не работает / «нет доступа к инструментам» | Ожидаемо для GigaChat 3: он понимает только легаси functions. Прокси транслирует сам; проверьте toolCallsTranslated в /stats |
500/422 на результатах функций |
GigaChat валидирует содержимое функции как JSON; не-JSON текст плагин оборачивает сам — если приходит всё равно, проверьте, что до API дошла трансляция (/stats) |
Ошибка в чате вида 422 status code (no body) |
Обычно это история сессии с tool-ходами, которые GigaChat не может перевалидировать в легаси-формате → начните новую сессию для этой модели. Прокси теперь возвращает понятное тело ошибки (OpenAI-формат {"error":{...}}) вместо пустого |
| dsh не запускается после установки | Аварийный откат; проверьте манифест профиля и чужие config:-патчи |
Лицензия
MIT