Fix OpenAI/LiteLLM provider: sanitize tool IDs, parse cache stats, hide empty cache UI

- Sanitize tool_use_id values in OpenAI request conversion to match
  Bedrock's required pattern ^[a-zA-Z0-9_-]+$. Fixes 400 errors when
  LiteLLM proxies to Bedrock and tool IDs contain invalid characters.

- Parse cache usage stats from LiteLLM/OpenAI responses
  (prompt_tokens_details.cached_tokens, cache_read_input_tokens,
  cache_creation_input_tokens) and propagate to token usage tracking.

- Hide cache-o-meter in session status bar when provider doesn't report
  cache data (LiteLLM/OpenAI), instead of showing misleading 0% stats.

- Update cost estimation to account for cache read/write pricing tiers.
This commit is contained in:
Ryan Ward
2026-07-20 13:36:11 -05:00
parent 1cc4ede83f
commit ca2cf6f8d6
3 changed files with 123 additions and 47 deletions
+19 -4
View File
@@ -4,6 +4,21 @@ use crate::ai::provider::types::{
ContentPart, ConversationMessage, MessageContent, MessageRole, ToolDefinition,
};
/// Sanitizes a tool_use_id to match Bedrock's required pattern `^[a-zA-Z0-9_-]+$`.
/// LiteLLM may proxy to Bedrock which rejects IDs with characters outside this set.
/// Replaces any invalid character with an underscore.
fn sanitize_tool_id(id: &str) -> String {
id.chars()
.map(|c| {
if c.is_ascii_alphanumeric() || c == '_' || c == '-' {
c
} else {
'_'
}
})
.collect()
}
pub fn build_openai_request(
messages: Vec<ConversationMessage>,
system_prompt: Option<String>,
@@ -75,7 +90,7 @@ fn convert_user_message(content: MessageContent) -> ConvertedMessages {
} => {
let mut msg = json!({
"role": "tool",
"tool_call_id": tool_use_id,
"tool_call_id": sanitize_tool_id(&tool_use_id),
"content": content,
});
if is_error {
@@ -117,7 +132,7 @@ fn convert_user_message(content: MessageContent) -> ConvertedMessages {
};
messages.push(json!({
"role": "tool",
"tool_call_id": tool_use_id,
"tool_call_id": sanitize_tool_id(&tool_use_id),
"content": result_content,
}));
}
@@ -157,7 +172,7 @@ fn convert_assistant_message(content: MessageContent) -> ConvertedMessages {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": tool_use_id,
"id": sanitize_tool_id(&tool_use_id),
"type": "function",
"function": {
"name": name,
@@ -190,7 +205,7 @@ fn convert_assistant_message(content: MessageContent) -> ConvertedMessages {
input,
} => {
tool_calls.push(json!({
"id": tool_use_id,
"id": sanitize_tool_id(&tool_use_id),
"type": "function",
"function": {
"name": name,