Metadata-Version: 2.5
Name: langchain-gigachat
Version: 0.5.2a1
Summary: An integration package connecting GigaChat and LangChain
Project-URL: Repository, https://github.com/ai-forever/langchain-gigachat
Project-URL: Source Code, https://github.com/ai-forever/langchain-gigachat/tree/master/libs/gigachat
License-Expression: MIT
License-File: LICENSE
Requires-Python: <4,>=3.10
Requires-Dist: gigachat==0.2.4a1
Requires-Dist: langchain-core<2,>=1
Description-Content-Type: text/markdown

<div align="center" id="top">

[![PyPI](https://img.shields.io/pypi/v/langchain-gigachat?style=flat-square)](https://pypi.org/project/langchain-gigachat/)
[![Python](https://img.shields.io/pypi/pyversions/langchain-gigachat?style=flat-square)](https://pypi.org/project/langchain-gigachat/)
[![CI](https://img.shields.io/github/actions/workflow/status/ai-forever/langchain-gigachat/check_diffs.yml?style=flat-square)](https://github.com/ai-forever/langchain-gigachat/actions/workflows/check_diffs.yml)
[![License](https://img.shields.io/github/license/ai-forever/langchain-gigachat?style=flat-square)](https://opensource.org/license/MIT)
[![Downloads](https://img.shields.io/pypi/dm/langchain-gigachat?style=flat-square)](https://pypistats.org/packages/langchain-gigachat)

</div>

# langchain-gigachat

LangChain integration for [GigaChat](https://giga.chat/) — a large language model.

This library is part of [GigaChain](https://github.com/ai-forever/gigachain) and wraps the [GigaChat Python SDK](https://github.com/ai-forever/gigachat) with LangChain-compatible interfaces.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Authentication](#authentication)
- [Usage Examples](#usage-examples)
  - [Chat](#chat)
  - [Streaming](#streaming)
  - [Async](#async)
  - [Embeddings](#embeddings)
  - [Reasoning Models](#reasoning-models)
  - [API v2 (`/v2/chat/completions`)](#api-v2-v2chatcompletions)
- [Tool Calling](#tool-calling)
  - [Legacy `bind_functions()`](#legacy-bind_functions)
- [Structured Output](#structured-output)
- [Attachments](#attachments)
  - [File Operations](#file-operations)
- [Configuration](#configuration)
  - [Constructor Parameters](#constructor-parameters)
  - [Environment Variables](#environment-variables)
- [Error Handling](#error-handling)
- [Related Projects](#related-projects)
- [Contributing](#contributing)
- [License](#license)

## Features

- **Chat completions** — synchronous and asynchronous, with streaming
- **Embeddings** — text vectorization via `GigaChatEmbeddings`
- **Tool calling** — standard LangChain `@tool` with GigaChat metadata in `extras`
- **Structured output** — Pydantic models and JSON mode
- **Reasoning models** — `reasoning_effort` for thinking models
- **Attachments** — images, audio, and documents via the Files API
- **File operations** — upload, list, retrieve, and delete files
- **Configurable retry** — exponential backoff via the underlying SDK
- **Environment-based configuration** — all parameters configurable via `GIGACHAT_` env vars
- **Fully typed** — Pydantic V2 models with `py.typed` marker

## Installation

```bash
pip install -U langchain-gigachat
```

This documentation includes the unreleased `0.5.2a1` prerelease.
For its examples, install the local sources and SDK `0.2.4a1` as described in
[example setup](../../examples/sdk_contract_alignment/README.md#setup).

**Requirements:** Python 3.10+

> **Note:** In production, keep TLS verification enabled (default).
> See [Authentication](#authentication) for certificate setup.

## Authentication

Set environment variables and let the SDK pick them up:

```bash
export GIGACHAT_CREDENTIALS="your-authorization-key"
export GIGACHAT_SCOPE="GIGACHAT_API_PERS"  # GIGACHAT_API_B2B or GIGACHAT_API_CORP for enterprise
```

After this, `GigaChat()` works without any arguments in code.

If your environment requires a specific TLS certificate:

```bash
export GIGACHAT_CA_BUNDLE_FILE="/path/to/certs.pem"
```

> **Warning:** Disabling TLS verification (`verify_ssl_certs=False`) is for local development only and is not recommended for production.

For detailed instructions on obtaining credentials and certificates, see the [GigaChat SDK](https://github.com/ai-forever/gigachat) and [API docs](https://developers.sber.ru/docs/ru/gigachat).

## Usage Examples

> The examples below assume authentication is configured via environment variables.
> See [Authentication](#authentication).

### Chat

```python
from langchain_gigachat import GigaChat

llm = GigaChat(credentials="your-authorization-key")

msg = llm.invoke("Hello, GigaChat!")
print(msg.content)
```

### Streaming

Receive tokens as they are generated:

```python
from langchain_gigachat import GigaChat

llm = GigaChat()

for chunk in llm.stream("Write a short poem about programming"):
    print(chunk.content, end="", flush=True)
print()
```

> **Note:** Wrapper-side local `stop` handling was removed in `0.5.x`. The
> public methods still accept `stop` for LangChain signature compatibility, but
> `langchain-gigachat` no longer applies stop-sequence truncation itself. See
> [`MIGRATION.md`](MIGRATION.md) before carrying `stop=...` call sites forward.

### Async

Use async/await for non-blocking operations:

```python
import asyncio

from langchain_gigachat import GigaChat


async def main():
    llm = GigaChat()
    msg = await llm.ainvoke("Explain quantum computing in simple terms.")
    print(msg.content)


asyncio.run(main())
```

### Embeddings

Generate vector representations of text:

```python
from langchain_gigachat import GigaChatEmbeddings

emb = GigaChatEmbeddings(model="Embeddings")

vector = emb.embed_query("Hello!")
print(len(vector))
```

### Reasoning Models

Use `reasoning_effort` with reasoning-capable models:

```python
from langchain_gigachat import GigaChat

llm = GigaChat(model="GigaChat-2-Reasoning", reasoning_effort="high")

msg = llm.invoke("How many r's are in the word 'strawberry'?")
print(msg.content)
print(msg.additional_kwargs.get("reasoning_content"))  # model's chain-of-thought
```

> **Note:** `reasoning_content` is also available during streaming — each `AIMessageChunk` carries it in `additional_kwargs`.

### API v2 (`/v2/chat/completions`)

For a runnable walkthrough, see the [API v2 feature notebook and v1 comparison](../../examples/api_v2_feature_comparison.ipynb)
with separate runnable recipes, readable answers, and side-by-side
examples for both routes. API calls run when you execute the corresponding cells.

The primary API v2 contract is opt-in; existing applications keep using the
legacy contract by default. Enable it on the model or on one bound runnable:

```python
from langchain_gigachat import GigaChat

llm = GigaChat(model="GigaChat-3-Ultra", use_api_v2=True)
print(llm.invoke("Hello!").content)

for chunk in llm.stream("Write one sentence about Lake Baikal."):
    print(chunk.text, end="", flush=True)

primary_once = GigaChat().bind(use_api_v2=True)
```

`ainvoke()` and `astream()` use the SDK's native async v2 resources.

Client functions continue to use LangChain tools. For a continuation, pass the
returned tool-call ID to `ToolMessage` unchanged:

```python
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool


@tool
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"{city}: sunny, 22C"


with_tools = llm.bind_tools([get_weather], tool_choice="get_weather")
question = HumanMessage("What is the weather in Moscow?")
assistant = with_tools.invoke([question])
call = assistant.tool_calls[0]
result = ToolMessage(
    content=get_weather.invoke(call["args"]),
    tool_call_id=call["id"],
    name=call["name"],
)
answer = llm.bind_tools([get_weather], tool_choice="auto").invoke(
    [question, assistant, result]
)
```

Provider-managed tools are also bound through the public API:

```python
with_search = llm.bind_tools(
    [{"type": "web_search"}],
    tool_choice="web_search",
)
response = with_search.invoke("Find recent GigaChat news.")
```

Built-in tools require `use_api_v2=True`. Client tool history is
route-specific: primary `tools_state_id` values are not translated to legacy
`functions_state_id` values.

Use native JSON Schema output with a Pydantic model:

```python
from pydantic import BaseModel


class City(BaseModel):
    name: str
    population: int


structured = llm.with_structured_output(City, method="json_schema")
city = structured.invoke("Return information about Kazan.")
```

On API v2, JSON mode can return an arbitrary JSON object without a user-provided
schema. The wrapper sends
`{"type": "json_schema", "schema": {"type": "object"}}`, satisfying the API's
required `schema` field. It leaves `strict` unset and parses the result as a dict:

```python
json_llm = llm.with_structured_output(None, method="json_mode")
value = json_llm.invoke("Return a JSON object with a short answer.")
assert isinstance(value, dict)
```

Existing file IDs work in standard `image`, `audio`, and `file` content blocks;
the existing experimental `auto_upload_attachments` path can also supply IDs to
v2 messages. Stateful requests accept `assistant_id=...` or
`storage={"thread_id": ...}`. For stored assistant/thread state, an instance
default model is omitted unless the invocation provides an explicit model.

API v2 supports `tool_choice="any"` / `"required"`, union argument schemas,
and `parallel_tool_calls=True`. Return one `ToolMessage` per call, preserving
each call ID.

API v2 accepts only a JSON object as a tool result. Text, markdown, numbers,
arrays and null are sent as `{"result": <value>}`; objects are sent as is.

Current limitations:

- parallel client function calls require distinct provider call IDs;
- parallel provider tools without explicit IDs are rejected as ambiguous;
- model support for native response formats is provider-dependent.

### SDK contract alignment

See the [five runnable examples](../../examples/sdk_contract_alignment/README.md)
for complete request/response flows and setup with SDK `0.2.4a1`.

The preview supports reasoning budgets, per-call IDs, parallel v2 functions,
`anyOf` tool schemas on v2, and provider response metadata. Generation controls
are placed inside `model_options` on v2; explicit nested values take priority.
Rare service options can be passed with `bind(additional_fields={...})` without
adding them to the model constructor.

With SDK `0.2.4a1`, use a stable session identifier for related requests:

```python
llm = GigaChat(use_api_v2=True, session_id="my-conversation", max_tokens=256)
```

`usage_metadata.input_tokens` includes cached input, and `cache_read` reports
its cached portion. Provider counters in the callback result
`LLMResult.llm_output["token_usage"]` retain their original billing semantics
([example](../../examples/sdk_contract_alignment/session_usage.py)). A session
makes caching possible; cache hits depend on the server and request contents.

See [SDK alignment and compatibility](MIGRATION.md#sdk-contract-alignment)
for requirements, request examples, and compatibility details.

## Tool Calling

Use the standard LangChain `@tool` decorator. Pass GigaChat-specific metadata via `extras`:

```python
from langchain_gigachat import GigaChat
from langchain_core.tools import tool


@tool(
    extras={
        "few_shot_examples": [{"request": "weather in Tokyo", "params": {"city": "Tokyo"}}]
    }
)
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"{city}: sunny, 22C"


llm = GigaChat()
llm_with_tools = llm.bind_tools([get_weather], tool_choice="auto")

msg = llm_with_tools.invoke("What's the weather in Tokyo?")
print(msg.tool_calls)
```

To configure function/tool ranking, pass `function_ranker` to `GigaChat`. For
example, disable ranking when binding tools:

```python
llm = GigaChat(function_ranker={"enabled": False})
llm_with_tools = llm.bind_tools([get_weather], tool_choice="auto")
```

> **Note:** API v2 maps `tool_choice="any"` and `"required"` to `tool_config.mode="any"` for client functions. API v1 supports `"auto"`, `"none"`, and a specific function name; its opt-in `allow_any_tool_choice_fallback=True` falls back to `"auto"` with a warning. Server and model capabilities still determine whether a request is supported.

> **Note:** API v1 accepts one function call per assistant message. With API v2, set `parallel_tool_calls=True` and return a `ToolMessage` for every call, using its original ID. The wrapper preserves the separate `tools_state_id` for continuation.

### Legacy `bind_functions()`

For legacy LangChain function-calling flows, `bind_functions()` is still available:

```python
from langchain_gigachat import GigaChat


def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"{city}: sunny, 22C"


llm = GigaChat()
llm_with_functions = llm.bind_functions(
    [get_weather],
    function_call="auto",
)
```

Use `bind_tools()` for new code. `bind_functions()` is kept as a compatibility layer over the provider's `function_call` transport and supports `None`, `"auto"`, `"none"`, or a specific function name.

On API v1, `ToolMessage` results are serialized as provider `function` messages
when continuing a conversation. API v2 serializes them as `tool` messages and
preserves the provider's tool-call ID.

## Structured Output

Extract typed data from model responses:

```python
from pydantic import BaseModel, Field

from langchain_gigachat import GigaChat


class Answer(BaseModel):
    """An answer with confidence score."""

    text: str = Field(description="Final answer")
    confidence: float = Field(ge=0, le=1, description="Confidence 0..1")


llm = GigaChat()
chain = llm.with_structured_output(Answer)

parsed = chain.invoke("What is the capital of France? Rate your confidence.")
print(parsed)
```

By default, `with_structured_output()` uses GigaChat function calling for
backward-compatible schema extraction. Native API-level JSON Schema constraints
are also available explicitly:

```python
llm.with_structured_output(Answer, method="json_schema")
```

> **Note:** `method="json_schema"` requires `gigachat>=0.2.1` and a model that
> supports the `response_format` API field. Support is currently in beta on
> GigaChat side and may not be available for every model — fall back to the
> default `method="function_calling"` if the API rejects the request.

`strict` defaults to `None` and is omitted from the request; pass it explicitly
with `method="json_schema"` when needed. Low-level
`response_format={"type": "json_schema", ...}` also requires a `schema`.

The legacy `method="json_mode"` with a supplied schema is still accepted for
backward compatibility, but it emits a `DeprecationWarning`; prefer
`method="json_schema"` for new code. Passing `None` with `method="json_mode"`
on API v2 uses the JSON object mode shown above and does not emit a warning.

## Attachments

Upload a file via the Files API, then reference it in `content_blocks`:

```python
from langchain_core.messages import HumanMessage

from langchain_gigachat import GigaChat

llm = GigaChat()

with open("image.png", "rb") as f:
    uploaded = llm.upload_file(("image.png", f.read()))

msg = HumanMessage(
    content_blocks=[
        {"type": "text", "text": "Describe the image."},
        {"type": "image", "file_id": uploaded.id_},
    ]
)

reply = llm.invoke([msg])
print(reply.content)
```

> **Note:** Supported `content_blocks` types: `image`, `audio`, `file`. The pattern is identical for each — only the `type` field differs.

> **Note:** Base64 data URLs in `image_url` / `audio_url` / `document_url` blocks can be auto-uploaded with `auto_upload_attachments=True`, but prefer explicit `upload_file()` in production.

### File Operations

Manage files via the Files API:

```python
from langchain_gigachat import GigaChat

llm = GigaChat()

# Upload
with open("document.pdf", "rb") as f:
    uploaded = llm.upload_file(("document.pdf", f.read()))
print(f"Uploaded: {uploaded.id_}")

# List
files = llm.list_files()
for f in files.data:
    print(f"{f.id_}: {f.filename}")

# Delete
llm.delete_file(uploaded.id_)
```

`get_file()` returns file metadata, while `get_file_content()` downloads file content:

```python
metadata = llm.get_file(uploaded.id_)          # UploadedFile
content = llm.get_file_content(uploaded.id_)   # Image with base64 payload
print(metadata.filename)
print(content.content[:20])
```

All file methods have async variants (`aget_file`, `aget_file_content`, `alist_files`, `adelete_file`, etc.).

## Configuration

All parameters can be passed to `GigaChat(...)` / `GigaChatEmbeddings(...)` directly or via environment variables with the `GIGACHAT_` prefix.

### Constructor Parameters

Most commonly used parameters (all are optional):

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `model` | `str` | `None` | Model name (e.g. `"GigaChat-2-Max"`, `"GigaChat-2-Pro"`) |
| `temperature` | `float` | `None` | Sampling temperature |
| `max_tokens` | `int` | `None` | Maximum number of tokens to generate |
| `top_p` | `float` | `None` | Nucleus sampling threshold (0.0–1.0) |
| `repetition_penalty` | `float` | `None` | Penalty applied to repeated tokens |
| `reasoning_effort` | `str` | `None` | Reasoning effort for reasoning models |
| `function_ranker` | `dict` | `None` | Function/tool ranking settings, e.g. `{"enabled": False}` to disable ranking |
| `credentials` | `str` | `None` | OAuth authorization key |
| `access_token` | `str` | `None` | Pre-obtained JWT token (bypasses OAuth) |
| `scope` | `str` | `None` | API scope (`GIGACHAT_API_PERS` / `_B2B` / `_CORP`) |
| `base_url` | `str` | `None` | Custom API endpoint |
| `verify_ssl_certs` | `bool` | `None` | TLS certificate verification |
| `ca_bundle_file` | `str` | `None` | Path to CA certificate bundle |
| `timeout` | `float` | `None` | Request timeout in seconds |
| `max_retries` | `int` | `None` | Retry attempts for transient errors (SDK default: `0`) |
| `retry_backoff_factor` | `float` | `None` | Exponential backoff multiplier (SDK default: `0.5`) |
| `profanity_check` | `bool` | `None` | Enable profanity filtering |
| `use_api_v2` | `bool` | `False` | Use the `/v2/chat/completions` contract |
| `session_id` | `str` | `None` | Default X-Session-ID; requires the aligned SDK |
| `reasoning_max_tokens` | `int` | `None` | Reasoning token budget |
| `parallel_tool_calls` | `bool` | `None` | Allow parallel client functions on API v2 |
| `streaming` | `bool` | `False` | Stream results by default |
| `auto_upload_attachments` | `bool` | `False` | Auto-upload base64 content from `image_url` / `audio_url` / `document_url` blocks |
| `allow_any_tool_choice_fallback` | `bool` | `False` | Convert v1 `tool_choice="any"` to `"auto"` with a warning |

For the full list of parameters (auth, SSL/mTLS, retry, flags, etc.), see the [GigaChat SDK README](https://github.com/ai-forever/gigachat#constructor-parameters) — the wrapper exposes the shared authentication and connection options.

### Environment Variables

All parameters can be configured via environment variables with the `GIGACHAT_` prefix (e.g. `GIGACHAT_CREDENTIALS`, `GIGACHAT_MODEL`, `GIGACHAT_BASE_URL`). See the [GigaChat SDK README](https://github.com/ai-forever/gigachat#environment-variables) for the full list.

`GIGACHAT_USE_API_V2=true/false` selects the default chat contract when the model
is created; `GigaChat(use_api_v2=...)` and invocation settings take priority.
Tool history is not migrated between v1 and v2: start a new conversation when
switching after tool calls.

> **Note:** Retries are handled by the underlying `gigachat` SDK. Don't combine them with LangChain `.with_retry()` — the attempts multiply:
>
> ```python
> llm = GigaChat(max_retries=3, retry_backoff_factor=0.5)  # delays: 0.5s, 1s, 2s
> ```

## Error Handling

SDK exceptions propagate unchanged through the LangChain wrapper (aligned with the `langchain-openai` approach):

```python
from gigachat.exceptions import AuthenticationError, RateLimitError, GigaChatException
from langchain_gigachat import GigaChat

llm = GigaChat()

try:
    llm.invoke("Hello!")
except AuthenticationError as e:
    print(f"Authentication failed: {e}")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except GigaChatException as e:
    print(f"GigaChat error: {e}")
```

For the full exception hierarchy and HTTP status code mapping, see the [GigaChat SDK — Error Handling](https://github.com/ai-forever/gigachat#error-handling).

## Tracing Metadata

When the provider returns tracing headers, the wrapper preserves them in both
non-streaming and streaming flows:

- `AIMessage.id` / `AIMessageChunk.id` carries `x-request-id`
- non-streaming responses keep full headers in `ChatResult.llm_output["x_headers"]`
- streaming responses expose full headers on the first chunk via
  `generation_info["x_headers"]`

This makes it possible to correlate LangChain runs with provider-side logs or
support requests without parsing SDK responses directly.

## Related Projects

- **[GigaChain](https://github.com/ai-forever/gigachain)** — a set of solutions for developing LLM applications and multi-agent systems, with support for LangChain, LangGraph, LangChain4j, GigaChat and other LLMs
- **[GigaChat Python SDK](https://github.com/ai-forever/gigachat)** — the underlying Python SDK that powers this integration
- [GigaChat API docs](https://developers.sber.ru/docs/ru/gigachat)

## Contributing

See [`CONTRIBUTING.md`](../../CONTRIBUTING.md). Development happens under `libs/gigachat`:

```bash
uv sync
make lint_package
make test
```

## License

This project is licensed under the MIT License.

Copyright © 2026 [GigaChain](https://github.com/ai-forever/gigachain)
