https://www.boundaryml.com logo
🟡 Issues with prompt caching using OpenRouter BYO...
# questions
p
hey folks, im using openrouter BYOK with anthropic as the main model for my agent and would like to utilize prompt caching, unfortunately, to use openrouters prompt caching for anthropic you need to pass through some cache_control breakpoint for it to work https://openrouter.ai/docs/guides/best-practices/prompt-caching#anthropic-claude
Copy code
Prompt caching with Anthropic requires the use of cache_control breakpoints. 
The cache_control breakpoint can only be inserted into the text part of a multipart message.
ive only been able to get this to work with the modular API in BAML but then that breaks BAML observability, is there something simpler that i should be doing to make this work?
a
can you share the code of how you made it work with modular api?
ah nevermind, i see their api
with an anthropic client i got it to add cache contorl like this: curl -X POST 'https://api.anthropic.com/v1/messages' -H "anthropic-version: 2023-06-01" -H "x-api-key: $REDACTED" -H "content-type: application/json" -d "{ \"model\": \"claude-3-haiku-20240307\", \"max_tokens\": 1000, \"messages\": [ { \"role\": \"user\", \"content\": [ { \"type\": \"text\", \"text\": \"Parse the following resume and return a structured representation of the data in the schema below.\" }, { \"type\": \"text\", \"text\": \"Resume:\n---\nJohn Doe\n\nEducation\n- University of California, Berkeley\n - B.S. in Computer Science\n - 2020\n\nSkills\n- Python\n- Java\n- C++\n---\n\nAnswer in JSON using this schema:\n{\n name: string,\n // Extract in the same order listed\n education: [\n {\n school: string,\n degree: string,\n year: int,\n }\n ],\n // Only include programming languages\n skills: string[],\n}\n\nJSON:\", \"cache_control\": { \"type\": \"ephemeral\" } } ] } ] }"
p
sure i can share it. thing is, that uses the anthropic client, when openrouters client is openai based. here's what i did with the modular API to get it working with openrouter:
Copy code
typescript
// Build the request using BAML's modular API
const req = await b.request.AgentLoop(
  bamlMessages,
  commandValues,
  conversationContext,
  currentDateTime,
  pdfAttachments,
  imageAttachments,
  { clientRegistry },
);

// Extract body and inject cache_control
const body = req.body.json();

// Find system message and split at my static doc marker
const systemMsgIndex = body.messages.findIndex(m => m.role === 'system');
if (systemMsgIndex !== -1) {
  const textContent = /* extract text from system msg */;
  const staticEndMarker = '</static_documentation>';
  const splitIndex = textContent.indexOf(staticEndMarker);

  if (splitIndex !== -1) {
    const staticContent = textContent.substring(0, splitIndex + staticEndMarker.length);
    const dynamicContent = textContent.substring(splitIndex + staticEndMarker.length);

    // Restructure with cache_control on static portion
    body.messages[systemMsgIndex] = {
      role: 'system',
      content: [
        { type: 'text', text: staticContent, cache_control: { type: 'ephemeral' } },
        { type: 'text', text: dynamicContent },
      ],
    };
  }
}

// Add anthropic-beta header required for caching
const headers = {
  ...req.headers,
  'anthropic-beta': 'prompt-caching-2024-07-31',
};

// Manual fetch
const response = await fetch(req.url, {
  method: req.method,
  headers,
  body: JSON.stringify(body),
});

// Parse with BAML
const decision = await b.parse.AgentLoop(responseContent);
this works and i get ~96% cache hit rates, but the downside is that BAML observability is broken because im bypassing the normal
b.AgentLoop()
call with a manual fetch. i can still use
traceAsync
and
setTags
but the built-in prompt/response logging in the boundary dashboard doesnt capture the actual LLM call. is there a cleaner way to inject
cache_control
into the request without losing observability? ideally something like:
Copy code
typescript
await b.AgentLoop(args, {
  modifyRequest: (body) => {
    // inject cache_control here
    return body;
  }
});
would love to know if there is a solution for this - know its a bit odd of a request - if theres a different BYOK router where this is possible let me know
a
Ok ill try this today with openrouter
v
for this specifically you can use the same way as anthoprics approach with your open ai clients as well!
Copy code
client<llm> MyClient {
  provider "openai-generic"
  options {
    base_url "https://openrouter.ai/api/v1"
    api_key env.OPENROUTER_API_KEY
    model "openai/gpt-5-mini"
    allowed_role_metadata ["cache_control"]
  }
}
https://cdn.discordapp.com/attachments/1451324383271325952/1451620508364968066/Screenshot_2025-12-19_at_9.01.32_AM.png?ex=6946d672&is=694584f2&hm=33649eaf1eb9463732c2e3a57c48d74905ae25649d0a07dd0f02be2af37649fb&
2 Views