lewtun HF Staff OpenAI Codex commited on
Commit
66cb9ac
·
unverified ·
1 Parent(s): 43f9cb0

Enable FAL prompt cache validation (#301)

Browse files

* Enable FAL prompt cache validation

Co-authored-by: OpenAI Codex <codex@openai.com>

* Address FAL prompt cache review

Co-authored-by: OpenAI Codex <codex@openai.com>

* Clarify session usage estimate copy

Co-authored-by: OpenAI Codex <codex@openai.com>

* Update usage estimate copy

Co-authored-by: OpenAI Codex <codex@openai.com>

---------

Co-authored-by: OpenAI Codex <codex@openai.com>

agent/core/prompt_caching.py CHANGED
@@ -1,8 +1,11 @@
1
  """Prompt-cache helpers for HF Router FAL requests.
2
 
3
  The HF Router/OpenRouter path uses provider-native prompt caching. Anthropic
4
- models need explicit JSON ``cache_control`` content blocks; OpenAI models cache
5
- eligible prefixes automatically and accept routing/retention hints in the body.
 
 
 
6
  Headers like ``X-OpenRouter-Cache`` control response caching, not prompt
7
  caching through this route.
8
  """
@@ -67,6 +70,9 @@ def with_prompt_cache_params(
67
  if _is_openai_gpt55(llm_params):
68
  updates["prompt_cache_key"] = stable_session_id
69
 
 
 
 
70
  if _is_openai_gpt55(llm_params):
71
  updates["prompt_cache_retention"] = "24h"
72
 
 
1
  """Prompt-cache helpers for HF Router FAL requests.
2
 
3
  The HF Router/OpenRouter path uses provider-native prompt caching. Anthropic
4
+ models keep explicit JSON ``cache_control`` content blocks for compatibility,
5
+ and also need the top-level ``cache_control`` hint on the OpenAI-compatible HF
6
+ Router path; the explicit markers alone are accepted there but do not produce
7
+ cache writes. OpenAI models cache eligible prefixes automatically and accept
8
+ routing/retention hints in the body.
9
  Headers like ``X-OpenRouter-Cache`` control response caching, not prompt
10
  caching through this route.
11
  """
 
70
  if _is_openai_gpt55(llm_params):
71
  updates["prompt_cache_key"] = stable_session_id
72
 
73
+ if _uses_explicit_cache_control(llm_params):
74
+ updates["cache_control"] = dict(_CACHE_CONTROL)
75
+
76
  if _is_openai_gpt55(llm_params):
77
  updates["prompt_cache_retention"] = "24h"
78
 
agent/core/telemetry.py CHANGED
@@ -52,14 +52,18 @@ def extract_usage(response_or_chunk: Any) -> dict:
52
 
53
  cache_read = _g("cache_read_input_tokens")
54
  cache_creation = _g("cache_creation_input_tokens")
55
-
56
- if not cache_read:
57
- details = _g("prompt_tokens_details", None)
58
- if details is not None:
59
- if isinstance(details, dict):
60
- cache_read = details.get("cached_tokens", 0) or 0
61
- else:
62
- cache_read = getattr(details, "cached_tokens", 0) or 0
 
 
 
 
63
 
64
  return {
65
  "prompt_tokens": int(prompt),
 
52
 
53
  cache_read = _g("cache_read_input_tokens")
54
  cache_creation = _g("cache_creation_input_tokens")
55
+ details = _g("prompt_tokens_details", None)
56
+
57
+ if not cache_read and details is not None:
58
+ if isinstance(details, dict):
59
+ cache_read = details.get("cached_tokens", 0) or 0
60
+ else:
61
+ cache_read = getattr(details, "cached_tokens", 0) or 0
62
+ if not cache_creation and details is not None:
63
+ if isinstance(details, dict):
64
+ cache_creation = details.get("cache_write_tokens", 0) or 0
65
+ else:
66
+ cache_creation = getattr(details, "cache_write_tokens", 0) or 0
67
 
68
  return {
69
  "prompt_tokens": int(prompt),
frontend/src/components/UsageMeter.tsx CHANGED
@@ -230,7 +230,7 @@ export default function UsageMeter() {
230
  Usage
231
  </Typography>
232
  <Typography variant="caption" color="text.secondary">
233
- Billing window resets when you switch back to a task.
234
  </Typography>
235
 
236
  {error ? (
 
230
  Usage
231
  </Typography>
232
  <Typography variant="caption" color="text.secondary">
233
+ Estimated from HF account usage per session.
234
  </Typography>
235
 
236
  {error ? (
tests/unit/test_prompt_caching.py CHANGED
@@ -182,10 +182,19 @@ def test_prompt_cache_params_add_session_id_for_fal_router_model():
182
  cached_params = with_prompt_cache_params(llm_params, session_id="session-1")
183
 
184
  assert cached_params is not llm_params
185
- assert cached_params["extra_body"] == {"session_id": "session-1"}
 
 
 
186
  assert "extra_body" not in llm_params
187
 
188
 
 
 
 
 
 
 
189
  def test_prompt_cache_params_merges_gpt55_cache_hints():
190
  llm_params = {
191
  **_gpt55_fal_params(),
 
182
  cached_params = with_prompt_cache_params(llm_params, session_id="session-1")
183
 
184
  assert cached_params is not llm_params
185
+ assert cached_params["extra_body"] == {
186
+ "session_id": "session-1",
187
+ "cache_control": {"type": "ephemeral"},
188
+ }
189
  assert "extra_body" not in llm_params
190
 
191
 
192
+ def test_prompt_cache_params_adds_anthropic_cache_control_without_session_id():
193
+ cached_params = with_prompt_cache_params(_anthropic_fal_params())
194
+
195
+ assert cached_params["extra_body"] == {"cache_control": {"type": "ephemeral"}}
196
+
197
+
198
  def test_prompt_cache_params_merges_gpt55_cache_hints():
199
  llm_params = {
200
  **_gpt55_fal_params(),
tests/unit/test_telemetry_usage.py CHANGED
@@ -13,6 +13,25 @@ class FakeSession:
13
  self.events.append(event)
14
 
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  @pytest.mark.asyncio
17
  async def test_record_hf_job_complete_emits_runtime_cost(monkeypatch):
18
  async def fake_catalog():
 
13
  self.events.append(event)
14
 
15
 
16
+ def test_extract_usage_reads_hf_router_cache_write_tokens():
17
+ response = SimpleNamespace(
18
+ usage=SimpleNamespace(
19
+ prompt_tokens=100,
20
+ completion_tokens=10,
21
+ total_tokens=110,
22
+ prompt_tokens_details=SimpleNamespace(
23
+ cached_tokens=80,
24
+ cache_write_tokens=20,
25
+ ),
26
+ )
27
+ )
28
+
29
+ usage = telemetry.extract_usage(response)
30
+
31
+ assert usage["cache_read_tokens"] == 80
32
+ assert usage["cache_creation_tokens"] == 20
33
+
34
+
35
  @pytest.mark.asyncio
36
  async def test_record_hf_job_complete_emits_runtime_cost(monkeypatch):
37
  async def fake_catalog():