Why do I need explicit breakpoints after both D and E in ABCDE to reuse cached ABCD?
Hi,
I am trying to understand why an explicit cache breakpoint after D must remain in the next request when I append E.
Using the same prompt_cache_key:
Request 1: ABCD/ -> writes ABCD
Request 2: ABCDE/ -> cached_tokens = 0, rewrites full ABCDE
However, if Request 2 keeps the old D breakpoint and adds E:
Request 1: ABCD/
Request 2: ABCD/E/ -> reads ABCD, writes only E
Here / means prompt_cache_breakpoint: {"mode":"explicit"}.
The A-D message text, roles, ordering, model, cache key, cache mode, and TTL are unchanged. Request 2 only appends E and moves the sole breakpoint from D to E.
One representative run returned:
ABCD/ seed: cached_tokens=0, cache_write_tokens=2233
ABCDE/ move probe: cached_tokens=0, cache_write_tokens=2497
ABCD/E/ retain probe: cached_tokens=2233, cache_write_tokens=264
Minimal reproduction
#!/usr/bin/env bash
set -euo pipefail
: "${OPENAI_API_KEY:?OPENAI_API_KEY is required}"
MODEL="${OPENAI_MODEL:-gpt-5.6-luna}"
ENDPOINT='https://api.openai.com/v1/responses'
NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]' | tr -d '-')
CACHE_KEY="explicit-breakpoint-repro:${NONCE}"
A_FILLER=$(printf 'foundation %.0s' {1..1400})
ECHO_FILLER=$(printf 'echo %.0s' {1..256})
A=$(printf 'RUN_NONCE=%s\nSEGMENT_A\n%s' "$NONCE" "$A_FILLER")
B=$(printf 'SEGMENT_B\n%s' "$ECHO_FILLER")
C=$(printf 'SEGMENT_C\n%s' "$ECHO_FILLER")
D=$(printf 'SEGMENT_D\n%s' "$ECHO_FILLER")
E=$(printf 'SEGMENT_E\n%s' "$ECHO_FILLER")
jq -n \
--arg model "$MODEL" \
--arg key "$CACHE_KEY" \
--arg a "$A" --arg b "$B" --arg c "$C" --arg d "$D" \
'{
model: $model,
instructions: "Reply with exactly OK.",
input: [$a, $b, $c, $d]
| map({type:"message", role:"user", content:[{type:"input_text", text:.}]}),
stream: true,
max_output_tokens: 64,
prompt_cache_key: $key,
prompt_cache_options: {mode:"explicit", ttl:"30m"}
}
| .input[3].content[0].prompt_cache_breakpoint = {mode:"explicit"}' \
> /tmp/oai-breakpoint-request-1.json
jq -n \
--arg model "$MODEL" \
--arg key "$CACHE_KEY" \
--arg a "$A" --arg b "$B" --arg c "$C" --arg d "$D" --arg e "$E" \
'{
model: $model,
instructions: "Reply with exactly OK.",
input: [$a, $b, $c, $d, $e]
| map({type:"message", role:"user", content:[{type:"input_text", text:.}]}),
stream: true,
max_output_tokens: 64,
prompt_cache_key: $key,
prompt_cache_options: {mode:"explicit", ttl:"30m"}
}
| .input[4].content[0].prompt_cache_breakpoint = {mode:"explicit"}' \
> /tmp/oai-breakpoint-request-2.json
curl --fail-with-body --silent --show-error --no-buffer \
"$ENDPOINT" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-H 'Content-Type: application/json' \
--data-binary @/tmp/oai-breakpoint-request-1.json \
--output /tmp/oai-breakpoint-response-1.sse
sleep 2
curl --fail-with-body --silent --show-error --no-buffer \
"$ENDPOINT" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-H 'Content-Type: application/json' \
--data-binary @/tmp/oai-breakpoint-request-2.json \
--output /tmp/oai-breakpoint-response-2.sse
for response in /tmp/oai-breakpoint-response-1.sse \
/tmp/oai-breakpoint-response-2.sse; do
sed -n 's/^data: \({.*\)$/\1/p' "$response" \
| jq 'select(.type == "response.completed")
| .response.usage.input_tokens_details'
done
My question is:
Is this the intended explicit-mode behavior? Does the service only attempt cache lookup at breakpoint positions that are still present in the current request, even when an earlier exact prefix was already cached under the same prompt_cache_key?
If so, does a client that resends full conversation history need to retain every earlier breakpoint it may want to reuse, producing ABCD/E/ rather than moving the breakpoint forward to ABCDE/