ai-forever / ai-forever/langchain-gigachat

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

Aperta
#73 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
bug
Lingua principale
Python
Stelle
50
Fork
13
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

### 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.)

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.