feat: NormalizedProvider

This commit is contained in:
leokun
2026-08-25 20:50:57 +08:00
parent 5f87357681
commit 081e1f50e2
8 changed files with 169 additions and 14 deletions
+106 -1
View File
@@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};
use super::{ModelSpec, ProjectedMessage, ToolDefinition};
use super::{ModelSpec, ProjectedContent, ProjectedMessage, ToolDefinition};
const PROVIDER_TOOL_CALL_ID_MAX_CHARS: usize = 64;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PromptSpec {
@@ -23,3 +25,106 @@ pub struct ModelInvocation {
pub provider_call_index: u64,
pub request: ModelRequest,
}
pub(crate) fn normalize_provider_tool_call_ids(history: &mut [ProjectedMessage]) {
for message in history {
match &mut message.content {
ProjectedContent::Assistant { calls, .. } => {
for call in calls {
truncate_tool_call_id(&mut call.call_id);
}
}
ProjectedContent::ToolResult(result) => {
truncate_tool_call_id(&mut result.call_id);
}
ProjectedContent::Parts(_) => {}
}
}
}
fn truncate_tool_call_id(call_id: &mut String) {
if let Some((end, _)) = call_id.char_indices().nth(PROVIDER_TOOL_CALL_ID_MAX_CHARS) {
call_id.truncate(end);
}
}
#[cfg(test)]
mod tests {
use super::normalize_provider_tool_call_ids;
use crate::model::{
ProjectedContent, ProjectedMessage, Role, ToolCallContent, ToolResultContent,
};
#[test]
fn provider_tool_call_ids_are_truncated_once_for_every_provider() {
let call_id = format!("cursor-tool-call:{}", "x".repeat(68));
assert_eq!(call_id.len(), 85);
let expected = call_id[..64].to_string();
let mut history = vec![
ProjectedMessage {
message_id: "assistant".into(),
role: Role::Assistant,
content: ProjectedContent::Assistant {
text: String::new(),
thinking: String::new(),
replay_state: None,
calls: vec![ToolCallContent {
index: 0,
call_id: call_id.clone(),
name: "Shell".into(),
arguments: serde_json::json!({}),
}],
},
},
ProjectedMessage {
message_id: "result".into(),
role: Role::Tool,
content: ProjectedContent::ToolResult(ToolResultContent {
call_id,
name: "Shell".into(),
content: "done".into(),
is_error: false,
image: None,
provider_parts: Vec::new(),
}),
},
];
normalize_provider_tool_call_ids(&mut history);
let ProjectedContent::Assistant { calls, .. } = &history[0].content else {
panic!("expected assistant message");
};
let ProjectedContent::ToolResult(result) = &history[1].content else {
panic!("expected tool result");
};
assert_eq!(calls[0].call_id, expected);
assert_eq!(result.call_id, expected);
}
#[test]
fn provider_tool_call_id_truncation_counts_unicode_characters() {
let mut history = vec![ProjectedMessage {
message_id: "assistant".into(),
role: Role::Assistant,
content: ProjectedContent::Assistant {
text: String::new(),
thinking: String::new(),
replay_state: None,
calls: vec![ToolCallContent {
index: 0,
call_id: format!("{}界y", "x".repeat(63)),
name: "Read".into(),
arguments: serde_json::json!({}),
}],
},
}];
normalize_provider_tool_call_ids(&mut history);
let ProjectedContent::Assistant { calls, .. } = &history[0].content else {
panic!("expected assistant message");
};
assert_eq!(calls[0].call_id, format!("{}界", "x".repeat(63)));
}
}