Skip to content

dsh-llm-gigachat

Verified

dsh-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.


Содержание

  1. Инструкция установки
  2. Как добавить провайдера Sber и какой ключ куда вставлять
  3. OAuth2: что происходит под капотом
  4. Конфигурация
  5. Откат / удаление плагина
  6. Troubleshooting

Установка

Вариант А. Из npm (рекомендуется)

dsh plugin --profile web add dsh-llm-gigachat

Устанавливает npm-пакет в профиль web. Затем добавьте плагин в список бандлов профиля ~/.dsh/profiles/web/package.jsondsh.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.jsondsh.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

  1. Зайдите в кабинет разработчика: developers.sber.ru → GigaChat API (или Sber Studio → раздел GigaChat).
  2. Создайте приложение / подключите API. Кабинет выдаст два значения:
    • client_id и client_secret;
    • либо сразу готовый «Ключ авторизации» / Authorization Key — это и есть та строка, что нам нужна.
  3. Уточните тип доступа, он задаёт 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 и список моделей. Остаётся только ключ:

  1. Запустите dsh web.
  2. Откройте Settings → Models.
  3. Нажмите Edit на строке Sber GigaChat.
  4. В поле API key вставьте ключ из шага 1 (значение base64(client_id:client_secret)).
  5. Нажмите Apply.

⚠️ Попытка создать через «Add a custom provider» ещё одного провайдера с id sber будет отклонена («идентификатор уже занят») — и это правильно: провайдер уже подключён плагином. Просто используйте существующую строку.

Вариант B. Без плагина (внешний gigachat-proxy.mjs + стандартный интерфейс)

Если плагин не установлен, но внешний прокси запущен (порт 8787), подключите провайдера через стандартную карточку:

  1. Settings → Models+ Add a custom provider.
  2. Provider ID: sber
  3. Display name: Sber GigaChat (или любое).
  4. Base URL: http://127.0.0.1:8787/v1 — именно локальный прокси, не https://api.giga.chat/v1/ (напрямую нельзя: pi-ai не делает OAuth2-обмен для hand-declared роутов — будет 401).
  5. API protocol: openai-completions.
  6. API key: ключ из шага 1.
  7. Models: нажмите Fetch available models — прокси отдаст список (GigaChat-3-Ultra, GigaChat-3-Pro); либо Add model и введите id вручную (хотя бы одну модель — без неё карточка не сохранится).
  8. 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. Выберите модель и проверьте

  1. В пикере модели (шапка чата) или в Settings → Models выберите: провайдер sber, модель GigaChat-3-Ultra (или GigaChat-3-Pro).
  2. Отправьте сообщение. Должен прийти ответ модели.

Проверка «живости» прокси (плагин поднимает его на 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.mutate path-операциями — конфиг остальных провайдеров (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 НЕ запускается)

  1. Любым редактором откройте ~/.dsh/profiles/web/package.json:
    • удалите строку "dsh-llm-gigachat": ... из dependencies;
    • удалите "dsh-llm-gigachat" из dsh.profile.bundles.
  2. Если вы вручную добавляли insert в ~/.dsh/profiles/web/cordis.patch.yml — удалите блок id: llm-gigachat.
  3. Переустановите зависимости и проверьте дерево без запуска dsh:
cd $env:USERPROFILE\.dsh\profiles\web
pnpm install
dsh --profile web --dump-config   # в выводе не должно быть llm-gigachat
  1. Запустите 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