ai-forever / ai-forever/langchain-gigachat

[Bug]: `with_structured_output` returns only first element of `list[str]` field

Đang mở
#73 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
bug
Ngôn ngữ chính
Python
Star
50
Fork
13
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

### Description

When using `GigaChat.with_structured_output()` with a Pydantic model that contains a `list[str]` field, the model consistently returns only the **first element** of the array, even when the prompt explicitly requests multiple items.

The same request sent via plain `client.invoke()` (without function calling) returns the full list correctly.

The issue is **not** related to the schema stripping bug from [#55](https://github.com/ai-forever/langchain-gigachat/issues/55) (fixed in `0.5.0`). The generated JSON schema is correct and contains `type: array` with `items: {type: string}`. Despite the valid schema, the GigaChat model fills the array with only the first element when responding via function calling. This can be confirmed by inspecting `result["raw"].additional_kwargs["function_call"]["arguments"]` — the truncated array is already present there, before any LangChain parsing.

### Steps to Reproduce

1. Define a Pydantic model with a `list[str]` field
2. Call `client.with_structured_output(MyModel)`
3. Invoke with a prompt that should produce multiple items
4. Observe that only the first element is returned

### Expected Behavior

```python
items = ["один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять", "десять"]
```

### Actual Behavior

```shell
{
"name": "NumberList",
"description": "List of numbers from one to ten",
"parameters": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": { "type": "string" },
"description": "List of strings"
}
}
}
}

But `function_call.arguments` returned by the model contains only one element:

{
"name": "NumberList",
"arguments": {
"items": ["один"]
}
}

Final parsed result:

items = ["один"] # 1 element instead of 10

Plain `invoke` with the same prompt returns the full list correctly:

items = ["один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять", "десять"]
```

### Code Example

```python
from __future__ import annotations
import json
from langchain_core.messages import AIMessage, HumanMessage
from langchain_gigachat import GigaChat
from langchain_gigachat.utils.function_calling import convert_to_gigachat_function
from pydantic import BaseModel, Field

class NumberList(BaseModel):
"""List of numbers from one to ten"""
items: list[str] = Field(description="List of strings")

# Print the function schema that gets sent to the API
print(json.dumps(convert_to_gigachat_function(NumberList), ensure_ascii=False, indent=2))

client = GigaChat(
base_url="...", # your endpoint
verify_ssl_certs=False,
profanity_check=False,
)

PROMPT = "Write all numbers from one to ten as words. Return JSON with key 'items'. Only JSON."

# Method A: with_structured_output — BROKEN
structured_llm = client.with_structured_output(NumberList, include_raw=True)
result_A = structured_llm.invoke(PROMPT)

raw: AIMessage = result_A["raw"]
print("function_call.arguments:", raw.additional_kwargs.get("function_call"))
print("tool_calls:", raw.tool_calls)
print(f"Method A items ({len(result_A['parsed'].items)}): {result_A['parsed'].items}")

# Method B: plain invoke — WORKS
response_B = client.invoke([HumanMessage(content=PROMPT)])
data_B = json.loads(response_B.content.strip())
print(f"Method B items ({len(data_B['items'])}): {data_B['items']}")
```

### langchain-gigachat Version

0.5.0

### Python Version

3.13

### Operating System

Linux

### Environment Details

| Package | Version |
|---|---|
| `langchain-gigachat` | `0.5.0` |
| `gigachat` | `0.2.0` |
| `langchain-core` | `1.3.0` |
| `pydantic` | `2.12.5` |
| Model | `GigaChat-2-Max`, `GigaChat-2-Max-Preview` , `GigaChat-2-Lite`, `GigaChat-2-Pro` |

### Additional Context

## Additional Context

- Both `GigaChat-2-Max` and `GigaChat-2-Max-Preview` exhibit the same behavior.
- The issue reproduces with any `list[str]` field regardless of field name or description.
- Workaround: use plain `client.invoke()` and parse JSON manually from `response.content`.
- Previously worked with an older code version that used plain invoke. The regression was introduced when the codebase switched to `with_structured_output`.

### Pre-submission Checklist

- [x] I have searched existing issues to ensure this is not a duplicate
- [x] I have provided all requested information above
- [x] I am using a supported Python version (3.10–3.13). (CI also runs 3.14 as experimental.)

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.