Curated chat history ends on an unanswered function_call when the turn after AFC is invalid

Đang mở
#3,314 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
48/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
backend-api-design

Hướng nghiên cứu

Read _BaseChat.__init__ and trace _extract_curated_history plus the four send_message variants that handle AFC. Compare their history truncation with record_history and the behavior discussed in #3290; start with tests/chats/test_send_message.py using the replay setup. Done means the curated history and next request handle an invalid turn after a function call consistently, with tests covering the exchange.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

When AFC runs a tool in Chat.send_message and the next model turn is invalid, curated history keeps the function_call and drops the computed function_response.

Dropping the response matches the _BaseChat.__init__ docstring (invalid turns "and their associated inputs"). The call is my question: 2580638 ends a turn on one on purpose when the budget is spent, before any function runs. Here it ran. A hand-built history passed to chats.create curates to the same tail, and #3290 leaves both.

Environment details
  • Programming language: Python
  • OS: Linux x86_64
  • Language runtime version: CPython 3.12.14
  • Package version: google-genai 3.0.0, main at afb10e8 (2026-11-07), also #3290 head 3eaeefa
Steps to reproduce
  1. Run the script below. The stubbed generate_content returns a function_call, then an empty SAFETY candidate.
# afc_tail.py
from unittest import mock
from google import genai
from google.genai import types

def get_weather(city: str) -> dict:
    """Returns the weather for a city."""
    return {"c": 21}

def kinds(history):
    return [c.role + ":" + ("+".join(k for p in c.parts or [] for k in ("text", "function_call", "function_response")
                                     if getattr(p, k) is not None) or "empty") for c in history]

call = types.GenerateContentResponse(candidates=[types.Candidate(content=types.Content(
    role="model", parts=[types.Part.from_function_call(name="get_weather", args={"city": "Paris"})]))])
blocked = types.GenerateContentResponse(candidates=[types.Candidate(finish_reason="SAFETY")])
ok = types.GenerateContentResponse(candidates=[types.Candidate(content=types.Content(role="model", parts=[types.Part(text="ok")]))])

client = genai.Client(api_key="not-a-real-key")
chat = client.chats.create(model="gemini-3.8-flash", config=types.GenerateContentConfig(tools=[get_weather]))
sent = []
def fake(*, model, contents, config):
    sent.append(list(contents))
    return [call, blocked, ok][len(sent) - 1]
with mock.patch.object(chat._modules, "generate_content", side_effect=fake):
    chat.send_message("what is the weather in Paris")
    print("comprehensive:", kinds(chat.get_history()))
    print("curated:      ", kinds(chat.get_history(curated=True)))
    chat.send_message("try again")
    print("next request: ", kinds(sent[-1]))
$ python afc_tail.py      # main afb10e8; the same three lines at #3290 head 3eaeefa
comprehensive: ['user:text', 'model:function_call', 'user:function_response', 'model:empty']
curated:       ['user:text', 'model:function_call']
next request:  ['user:text', 'model:function_call', 'user:text']

With a trial truncation in sync send_message back to the curated length before the AFC loop:

$ python afc_tail.py      # main afb10e8 plus that truncation in Chat.send_message
curated:       []
next request:  ['user:text']
# not run: the live API, so I have not measured how the server answers that next request
# not run: tests/chats/test_send_message.py, which needs the replay setup

I would drop the whole exchange, as record_history already does with an invalid turn's input. The cost is that a retry may run the tool again. That needs the truncation in all four send_message variants, and _extract_curated_history popping through the call when it pops a function response. Is the tail intended once the function ran, or should the exchange go?

Ngôn ngữ chính
Python
Star
4.1k
Fork
1.1k
Merge trung bình
1 ngày 21 giờ
Pull request đã merge (30 ngày)
103

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của googleapis/python-genai

Tất cả issue của googleapis/python-genai

Issue tương tự

Thêm issue về Python

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.