GigaChat client with certificate pool, shared limits and stop events
Reason this release was yanked:
deprecated
Project description
gigamux
Асинхронный клиент GigaChat: пул сертификатов-каналов, общие in-flight лимиты через Redis (coredis) с fallback на локальные счётчики, stop-события (429 + время блокировки) и drop-in адаптеры LangChain. Сервисы переходят на библиотеку заменой импорта, retry-логика и троттлинг живут в ядре.
Quick start
Ядро напрямую:
from gigamux import GigaCoreClient, config_from_env, ChatRequest, Message
config = config_from_env()
async with GigaCoreClient(config) as client:
result = await client.chat(
ChatRequest(
model="GigaChat-2-Max",
messages=(Message(role="user", content="привет"),),
)
)
print(result.content, result.meta.channel)
LangChain-сервисы (адаптеры импортируются напрямую, требуют extra langchain):
from gigamux.adapters.langchain_chat import PooledGigaChat
llm = PooledGigaChat(core=client, model="GigaChat-2-Max", temperature=0.1)
message = await llm.ainvoke("привет")
Индексация (один батч = один HTTP-запрос = один слот, нарезка по 20 в адаптере):
from gigamux.adapters.embeddings import PooledGigaEmbeddings
embedder = PooledGigaEmbeddings(core=client, model="EmbeddingsGigaR")
vectors = await embedder.aembed_documents(texts)
Function calling и structured output
PooledGigaChat поддерживает инструменты и структурированный вывод —
паритет с langchain-gigachat, включая react-агентов LangGraph:
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
@tool
def get_weather(city: str) -> str:
"""Return current weather for a city."""
...
agent = create_react_agent(llm, [get_weather])
state = await agent.ainvoke({"messages": [...]})
Extra gigamux[langchain] ставит только langchain-core; для примера выше
нужен отдельно установленный langgraph (pip install langgraph).
Структурированный вывод доступен двумя методами эталонной библиотеки:
function_calling (по умолчанию) и json_mode (через response_format).
chain = llm.with_structured_output(MyModel)
chain = llm.with_structured_output(MyModel, method="json_mode")
result = await chain.ainvoke("...")
Ограничения и семантика:
- Стриминг с инструментами на уровне API невозможен, поэтому
astream/astream_eventsс привязанными функциями прозрачно выполняют обычный вызов и отдают ответ одним чанком: токенного стриминга нет, но react-агенты подastream_events/stream_mode="messages"работают. - Параллельные tool_calls невозможны на стороне GigaChat: больше одного
tool_call в сообщении —
ValueError. - В
with_structured_outputподдерживаются pydantic-модели обоих поколений (v2 иpydantic.v1) в обоих методах; v1-класс возвращается экземпляром, как и v2. finish_reasonдоступен вresponse_metadataкаждого ответа адаптера.with_structured_outputметодомfunction_callingвозвращаетNone, если модель ответила текстом вместо вызова функции (поведение эталона); сinclude_raw=Trueсырое сообщение доступно вoutput["raw"].json_modeиспользует бета-фичу APIresponse_format— при её отсутствии на контуре ошибка идентична поведению langchain-gigachat.few_shot_examplesпробрасываются изmetadataинструмента.
Стриминг
Ядро отдаёт дельты как StreamChunk; слот канала занят весь стрим, метаданные
приходят в последнем чанке:
request = ChatRequest(
model="GigaChat-2-Max",
messages=(Message(role="user", content="расскажи анекдот"),),
)
async for chunk in client.stream(request):
print(chunk.delta, end="")
if chunk.meta is not None:
print(chunk.meta.channel, chunk.meta.duration_ms)
Ошибки до первого чанка ретраятся как обычные запросы; после начала стрима —
StreamInterruptedError без молчаливого ретрая. У адаптера работает
llm.astream(...); без инструментов это настоящий токенный стриминг, а с
привязанными функциями ответ приходит одним финальным чанком (см. раздел
про function calling).
Потребитель, который может бросить стрим до конца, должен оборачивать его в
contextlib.aclosing(...), иначе слот освобождается только сборщиком мусора.
Keepalive слота ограничен slot_max_lifetime (дефолт 900 с): после этого
лимита lease перестаёт продлеваться и истекает по TTL даже без финализации
генератора.
Конфигурация в коде
config_from_env() — удобный путь, но конфиг можно собрать руками:
from gigamux import ChannelConfig, ClientConfig, GigaCoreClient
config = ClientConfig(
channels=[
ChannelConfig(
name="cert-a",
base_url="https://giga.internal:10501/v1",
cert_file="/etc/certs/a/tls.pem",
key_file="/etc/certs/a/key.pem",
ca_bundle="/etc/certs/ca.pem",
limits={"GigaChat-2-Max": 2, "EmbeddingsGigaR": 4},
),
ChannelConfig(
name="cert-b",
base_url="https://giga.internal:10502/v1",
limits={"*": 3},
),
],
redis_url="rediss://redis.internal:6380/0",
redis_username="svc",
redis_password="...",
acquire_timeout=30.0,
max_retries=3,
)
async with GigaCoreClient(config) as client:
...
Ключевые поля ClientConfig (все имеют дефолты):
| Поле | Дефолт | Назначение |
|---|---|---|
acquire_timeout |
30.0 | сколько ждать свободный слот; None — ждать без таймаута (индексация) |
lease_ttl / heartbeat_interval |
120 / ttl/3 | время жизни слота в Redis и период продления |
slot_max_lifetime |
900.0 | максимум продления слота keepalive-ом; дальше lease истекает по TTL |
max_retries / backoff_factor / backoff_max |
3 / 1.0 / 30.0 | ретраи 429/5xx с экспоненциальной паузой |
connect_timeout / read_timeout |
10 / 120 | таймауты HTTP |
redis_failure_threshold / redis_probe_interval |
3 / 15.0 | circuit breaker: после N ошибок Redis лимиты локальные, проба каждые 15 с |
limits канала — единственный источник правды о том, какие модели канал
обслуживает: ключ — имя модели, значение — число одновременных запросов,
"*" — wildcard. cert_file/key_file задаются только вместе; без них
канал работает без mTLS. acquire_timeout можно переопределить и на один
вызов: client.chat(request, acquire_timeout=None).
Переменные окружения
| Переменная | Назначение |
|---|---|
GIGA_CHANNELS |
JSON-список каналов (name, base_url, cert_file, key_file, ca_bundle, limits) |
GIGA_REDIS_URL |
URL Redis для распределённых лимитов; не задан -> локальные лимиты |
GIGA_REDIS_USERNAME / GIGA_REDIS_PASSWORD |
аутентификация Redis |
GIGA_REDIS_KEY_FILE / GIGA_REDIS_CERT_FILE / GIGA_REDIS_CA_BUNDLE |
mTLS для Redis |
GIGACHAT_HOST / GIGACHAT_PORT / GIGACHAT_TLS_CERT_FILEPATH / GIGACHAT_KEY_FILEPATH / GIGACHAT_CA_BUNDLE_FILEPATH |
режим совместимости: один канал из legacy-переменных |
GIGACHAT_ENDPOINT |
legacy-путь base_url в режиме совместимости (по умолчанию /v1) |
GIGACHAT_MAX_CONCURRENCY |
лимит на канал в режиме совместимости (wildcard-модель) |
Если задан GIGA_CHANNELS, он имеет приоритет. Иначе из GIGACHAT_* строится
пул из одного канала с wildcard-лимитом, что даёт миграцию без изменения конфига.
Семантика
Лимит — in-flight слоты на пару канал+модель, общие между репликами. Слот берётся
как lease с TTL в Redis и продлевается heartbeat-ом, поэтому упавший под не держит
слот навсегда. При занятых слотах вызов ждёт в очереди с джиттером до
acquire_timeout (онлайн ~30 с, индексация — без таймаута), затем SlotWaitTimeoutError.
Stop-событие выводит канал из ротации до времени T и прозрачно ретраит на другом
канале; когда все каналы в стопе — сразу AllChannelsStoppedError(resume_at).
Ошибка посреди стрима не ретраится молча — StreamInterruptedError.
Ошибки
Все исключения наследуют GigaClientError и импортируются из gigamux:
| Исключение | Когда летит |
|---|---|
ConfigError |
невалидная конфигурация или переменные окружения |
NoChannelForModelError |
ни один канал не обслуживает запрошенную модель |
SlotWaitTimeoutError |
свободный слот не появился за acquire_timeout |
AllChannelsStoppedError |
все каналы в стопе; resume_at — когда вернётся первый |
RateLimitError |
429 пережил все ретраи |
ServerError |
5xx или сетевая ошибка пережили все ретраи |
ApiError |
прочие неретраябельные ответы API (4xx); базовый класс двух предыдущих |
StreamInterruptedError |
стрим оборвался после уже отданных чанков |
У ApiError и наследников доступны status_code, body, channel,
request_id. Типовая обработка: SlotWaitTimeoutError и AllChannelsStoppedError
— перегрузка, имеет смысл отдать 429/503 наверх; ApiError — ошибка запроса,
ретраить бесполезно.
Метаданные ответа
Каждый результат (ChatResult, EmbedResult, финальный StreamChunk) несёт
meta: ResponseMeta — request_id, headers, channel, model, attempts,
duration_ms, status_code. Это сквозной способ узнать, через какой канал
ушёл запрос и сколько он занял, например для логов и метрик.
Тесты
python -m venv .venv
.venv\Scripts\pip install -e ".[dev]"
.venv\Scripts\python -m pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gigamux-0.2.0.tar.gz.
File metadata
- Download URL: gigamux-0.2.0.tar.gz
- Upload date:
- Size: 47.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2aeff12efc372ee7305d4c5f28efb7faaa884fafccee2468f58fafe7150c67ff
|
|
| MD5 |
619fc24c89f03b005db2ad27814f1f7f
|
|
| BLAKE2b-256 |
dc1a3dd7d3f8dcce9e13bb54dc443e45f1aceeffebd572f64bd69549715760fd
|
File details
Details for the file gigamux-0.2.0-py3-none-any.whl.
File metadata
- Download URL: gigamux-0.2.0-py3-none-any.whl
- Upload date:
- Size: 36.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0081326e55efd30f1fe661728f173d405e5ff1d41b7349b7005e002dd0f5a17
|
|
| MD5 |
fb7f12c4450828721c60d427f00f8ece
|
|
| BLAKE2b-256 |
7bd9a95eead00ae6a3d81538ac93f36262e27bf29e6848d04d06348e26ddfaf1
|