Skip to content

[Bug] GeminiClient leaves thinking_config=None, so gemini-2.5-* truncates structured output mid-JSON #1868

Description

@drmm

Summary

GeminiClient leaves thinking_config at None, which means "model default". On the gemini-2.5-* family the default is thinking ON, and thinking tokens are drawn from the same output budget as the answer. Graphiti's structured-extraction prompts return long JSON, so the JSON is cut mid-object and the run dies parsing it.

Version: graphiti-core 0.30.2 (repo at eaa4128, "Bump graphiti-core to 0.30.2 (#1858)").

Where

  • graphiti_core/llm_client/gemini_client.py:100thinking_config: types.ThinkingConfig | None = None
  • graphiti_core/llm_client/gemini_client.py:126self.thinking_config = thinking_config
  • graphiti_core/llm_client/gemini_client.py:299 — passed straight through to GenerateContentConfig(..., thinking_config=self.thinking_config)

Nothing between those lines distinguishes "caller did not ask for thinking" from "thinking off". None reaches the API, the API applies the model default, and on gemini-2.5-flash that is thinking enabled.

Symptom

Any sufficiently long extraction (a multi-KB document) fails with a JSON parse error whose column is wherever the budget ran out:

Expecting value: line 1 column 2071 (char 2070)

Retrying does not help: the truncation point moves but the failure is the same. It looks like a malformed-response bug in the LLM client, which is why it is easy to misattribute.

Reproduction

Quickstart with GeminiClient(config=LLMConfig(api_key=..., model='gemini-2.5-flash', temperature=0)) and add_episode on a document of roughly 10 KB or more. Ingesting an 81-document corpus, this was ~100% fatal until fixed.

Fix that works

from google.genai import types as genai_types

GeminiClient(
    config=LLMConfig(api_key=key, model='gemini-2.5-flash', temperature=0),
    thinking_config=genai_types.ThinkingConfig(thinking_budget=0),
)

With thinking_budget=0 the same 81-document corpus ingests cleanly.

Suggested change

Default thinking_budget=0 for gemini-2.5-* when the caller passes no thinking_config, rather than deferring to the model default — structured extraction wants the whole budget spent on the schema-shaped answer. A caller who wants thinking can still pass a ThinkingConfig explicitly. Failing that, _resolve_max_tokens (gemini_client.py:175) could account for the thinking allocation, and the docstring at line 110 could warn that None means thinking on for 2.5 models, not off.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions