Additional cleanup of caching
This commit is contained in:
@@ -134,6 +134,9 @@ pub struct RequestParams {
|
|||||||
/// Full Bedrock conversation history for direct Bedrock calls.
|
/// Full Bedrock conversation history for direct Bedrock calls.
|
||||||
/// When present, the Bedrock path uses this instead of extracting from task_context.
|
/// When present, the Bedrock path uses this instead of extracting from task_context.
|
||||||
pub bedrock_message_history: Vec<crate::ai::bedrock::convert::ConversationMessage>,
|
pub bedrock_message_history: Vec<crate::ai::bedrock::convert::ConversationMessage>,
|
||||||
|
/// Compacted summary from a prior summarization pass. Injected into the system
|
||||||
|
/// prompt so it benefits from system-level caching.
|
||||||
|
pub bedrock_compact_summary: Option<String>,
|
||||||
/// Populated by the Bedrock path after building the message list.
|
/// Populated by the Bedrock path after building the message list.
|
||||||
/// Contains the full messages sent (old history + new input) so the controller
|
/// Contains the full messages sent (old history + new input) so the controller
|
||||||
/// can store them back into the conversation for the next request cycle.
|
/// can store them back into the conversation for the next request cycle.
|
||||||
@@ -328,6 +331,7 @@ impl RequestParams {
|
|||||||
parent_agent_id: None,
|
parent_agent_id: None,
|
||||||
agent_name: None,
|
agent_name: None,
|
||||||
bedrock_message_history: Vec::new(),
|
bedrock_message_history: Vec::new(),
|
||||||
|
bedrock_compact_summary: None,
|
||||||
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
||||||
is_summarization: false,
|
is_summarization: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ pub async fn generate_multi_agent_output(
|
|||||||
model_id,
|
model_id,
|
||||||
root_task_id: params.root_task_id.clone(),
|
root_task_id: params.root_task_id.clone(),
|
||||||
bedrock_message_history: params.bedrock_message_history.clone(),
|
bedrock_message_history: params.bedrock_message_history.clone(),
|
||||||
|
bedrock_compact_summary: params.bedrock_compact_summary.clone(),
|
||||||
bedrock_messages_sent: params.bedrock_messages_sent.clone(),
|
bedrock_messages_sent: params.bedrock_messages_sent.clone(),
|
||||||
is_summarization: params.is_summarization,
|
is_summarization: params.is_summarization,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ fn request_params_with_ask_user_question_enabled(ask_user_question_enabled: bool
|
|||||||
parent_agent_id: None,
|
parent_agent_id: None,
|
||||||
agent_name: None,
|
agent_name: None,
|
||||||
bedrock_message_history: Vec::new(),
|
bedrock_message_history: Vec::new(),
|
||||||
|
bedrock_compact_summary: None,
|
||||||
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
||||||
is_summarization: false,
|
is_summarization: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,6 +241,11 @@ pub struct AIConversation {
|
|||||||
/// context window size, NOT a cumulative total.
|
/// context window size, NOT a cumulative total.
|
||||||
current_context_tokens: u32,
|
current_context_tokens: u32,
|
||||||
|
|
||||||
|
/// When set, contains the compacted summary of prior conversation history.
|
||||||
|
/// Injected into the system prompt (not the messages array) so it benefits
|
||||||
|
/// from system-level caching at the 1-hour TTL.
|
||||||
|
compact_summary: Option<String>,
|
||||||
|
|
||||||
/// Guards against repeated auto-compact triggers within the same high-usage window.
|
/// Guards against repeated auto-compact triggers within the same high-usage window.
|
||||||
/// Set to true when auto-compact fires; reset when summarization completes.
|
/// Set to true when auto-compact fires; reset when summarization completes.
|
||||||
has_pending_auto_compact: bool,
|
has_pending_auto_compact: bool,
|
||||||
@@ -298,6 +303,7 @@ impl AIConversation {
|
|||||||
is_remote_child: false,
|
is_remote_child: false,
|
||||||
last_event_sequence: None,
|
last_event_sequence: None,
|
||||||
bedrock_message_history: Vec::new(),
|
bedrock_message_history: Vec::new(),
|
||||||
|
compact_summary: None,
|
||||||
current_context_tokens: 0,
|
current_context_tokens: 0,
|
||||||
has_pending_auto_compact: false,
|
has_pending_auto_compact: false,
|
||||||
subagent_retry_count: 0,
|
subagent_retry_count: 0,
|
||||||
@@ -498,6 +504,7 @@ impl AIConversation {
|
|||||||
is_remote_child: false,
|
is_remote_child: false,
|
||||||
last_event_sequence,
|
last_event_sequence,
|
||||||
bedrock_message_history,
|
bedrock_message_history,
|
||||||
|
compact_summary: None,
|
||||||
current_context_tokens: 0,
|
current_context_tokens: 0,
|
||||||
has_pending_auto_compact: false,
|
has_pending_auto_compact: false,
|
||||||
subagent_retry_count: 0,
|
subagent_retry_count: 0,
|
||||||
@@ -525,6 +532,14 @@ impl AIConversation {
|
|||||||
self.bedrock_message_history.extend(messages);
|
self.bedrock_message_history.extend(messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compact_summary(&self) -> Option<&str> {
|
||||||
|
self.compact_summary.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_compact_summary(&mut self, summary: Option<String>) {
|
||||||
|
self.compact_summary = summary;
|
||||||
|
}
|
||||||
|
|
||||||
/// Assigns fresh exchange IDs to all exchanges in this conversation.
|
/// Assigns fresh exchange IDs to all exchanges in this conversation.
|
||||||
/// Used when forking conversations to avoid ID collisions with persisted blocks.
|
/// Used when forking conversations to avoid ID collisions with persisted blocks.
|
||||||
pub fn reassign_exchange_ids(&mut self) {
|
pub fn reassign_exchange_ids(&mut self) {
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ impl BedrockClient {
|
|||||||
needs_create_task: bool,
|
needs_create_task: bool,
|
||||||
messages: Vec<ConversationMessage>,
|
messages: Vec<ConversationMessage>,
|
||||||
system_prompt: Option<String>,
|
system_prompt: Option<String>,
|
||||||
|
compact_summary: Option<String>,
|
||||||
tools: Vec<ToolDefinition>,
|
tools: Vec<ToolDefinition>,
|
||||||
max_tokens: i32,
|
max_tokens: i32,
|
||||||
temperature: Option<f32>,
|
temperature: Option<f32>,
|
||||||
@@ -158,6 +159,7 @@ impl BedrockClient {
|
|||||||
let converted = build_converse_request(
|
let converted = build_converse_request(
|
||||||
messages.clone(),
|
messages.clone(),
|
||||||
system_prompt.clone(),
|
system_prompt.clone(),
|
||||||
|
compact_summary,
|
||||||
tools.clone(),
|
tools.clone(),
|
||||||
max_tokens,
|
max_tokens,
|
||||||
temperature,
|
temperature,
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ pub enum ContentPart {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ToolDefinition {
|
pub struct ToolDefinition {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
@@ -69,6 +69,7 @@ pub struct ToolDefinition {
|
|||||||
pub fn build_converse_request(
|
pub fn build_converse_request(
|
||||||
messages: Vec<ConversationMessage>,
|
messages: Vec<ConversationMessage>,
|
||||||
system_prompt: Option<String>,
|
system_prompt: Option<String>,
|
||||||
|
compact_summary: Option<String>,
|
||||||
tools: Vec<ToolDefinition>,
|
tools: Vec<ToolDefinition>,
|
||||||
max_tokens: i32,
|
max_tokens: i32,
|
||||||
temperature: Option<f32>,
|
temperature: Option<f32>,
|
||||||
@@ -76,7 +77,7 @@ pub fn build_converse_request(
|
|||||||
stop_sequences: Option<Vec<String>>,
|
stop_sequences: Option<Vec<String>>,
|
||||||
) -> ConvertedRequest {
|
) -> ConvertedRequest {
|
||||||
let bedrock_messages = convert_messages(messages);
|
let bedrock_messages = convert_messages(messages);
|
||||||
let system = convert_system_prompt(system_prompt);
|
let system = convert_system_prompt(system_prompt, compact_summary);
|
||||||
let inference_config = build_inference_config(max_tokens, temperature, top_p, stop_sequences);
|
let inference_config = build_inference_config(max_tokens, temperature, top_p, stop_sequences);
|
||||||
let tool_config = build_tool_config(tools);
|
let tool_config = build_tool_config(tools);
|
||||||
|
|
||||||
@@ -267,22 +268,35 @@ fn coalesce_consecutive_roles(messages: Vec<BedrockMessage>) -> Vec<BedrockMessa
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_system_prompt(system_prompt: Option<String>) -> Vec<SystemContentBlock> {
|
fn convert_system_prompt(
|
||||||
match system_prompt {
|
system_prompt: Option<String>,
|
||||||
Some(prompt) if !prompt.is_empty() => {
|
compact_summary: Option<String>,
|
||||||
vec![
|
) -> Vec<SystemContentBlock> {
|
||||||
SystemContentBlock::Text(prompt),
|
let mut blocks = Vec::new();
|
||||||
SystemContentBlock::CachePoint(
|
|
||||||
CachePointBlock::builder()
|
if let Some(prompt) = system_prompt {
|
||||||
.r#type(CachePointType::Default)
|
if !prompt.is_empty() {
|
||||||
.ttl(CacheTtl::OneHour)
|
blocks.push(SystemContentBlock::Text(prompt));
|
||||||
.build()
|
|
||||||
.expect("valid cache point"),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
_ => vec![],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(summary) = compact_summary {
|
||||||
|
blocks.push(SystemContentBlock::Text(format!(
|
||||||
|
"<conversation-summary>\n{summary}\n</conversation-summary>"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !blocks.is_empty() {
|
||||||
|
blocks.push(SystemContentBlock::CachePoint(
|
||||||
|
CachePointBlock::builder()
|
||||||
|
.r#type(CachePointType::Default)
|
||||||
|
.ttl(CacheTtl::OneHour)
|
||||||
|
.build()
|
||||||
|
.expect("valid cache point"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_inference_config(
|
fn build_inference_config(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fn test_text_message_converts_to_single_block() {
|
|||||||
content: MessageContent::Text("Hello".to_string()),
|
content: MessageContent::Text("Hello".to_string()),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
assert_eq!(result.messages.len(), 1);
|
assert_eq!(result.messages.len(), 1);
|
||||||
assert_eq!(result.messages[0].role(), &ConversationRole::User);
|
assert_eq!(result.messages[0].role(), &ConversationRole::User);
|
||||||
@@ -29,7 +29,7 @@ fn test_tool_use_produces_valid_json_input() {
|
|||||||
},
|
},
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
assert_eq!(result.messages.len(), 1);
|
assert_eq!(result.messages.len(), 1);
|
||||||
assert_eq!(result.messages[0].role(), &ConversationRole::Assistant);
|
assert_eq!(result.messages[0].role(), &ConversationRole::Assistant);
|
||||||
@@ -53,7 +53,7 @@ fn test_tool_result_with_matching_id() {
|
|||||||
},
|
},
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
assert_eq!(result.messages.len(), 1);
|
assert_eq!(result.messages.len(), 1);
|
||||||
match &result.messages[0].content()[0] {
|
match &result.messages[0].content()[0] {
|
||||||
@@ -75,7 +75,7 @@ fn test_tool_result_error_status() {
|
|||||||
},
|
},
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
match &result.messages[0].content()[0] {
|
match &result.messages[0].content()[0] {
|
||||||
ContentBlock::ToolResult(block) => {
|
ContentBlock::ToolResult(block) => {
|
||||||
@@ -101,7 +101,7 @@ fn test_consecutive_same_role_messages_coalesced() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
assert_eq!(result.messages.len(), 1);
|
assert_eq!(result.messages.len(), 1);
|
||||||
assert_eq!(result.messages[0].content().len(), 2);
|
assert_eq!(result.messages[0].content().len(), 2);
|
||||||
@@ -126,7 +126,7 @@ fn test_alternating_roles_not_coalesced() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
assert_eq!(result.messages.len(), 3);
|
assert_eq!(result.messages.len(), 3);
|
||||||
assert_eq!(result.messages[0].role(), &ConversationRole::User);
|
assert_eq!(result.messages[0].role(), &ConversationRole::User);
|
||||||
@@ -144,6 +144,7 @@ fn test_system_prompt_separated_from_messages() {
|
|||||||
let result = build_converse_request(
|
let result = build_converse_request(
|
||||||
messages,
|
messages,
|
||||||
Some("You are a helpful assistant.".to_string()),
|
Some("You are a helpful assistant.".to_string()),
|
||||||
|
None,
|
||||||
vec![],
|
vec![],
|
||||||
4096,
|
4096,
|
||||||
None,
|
None,
|
||||||
@@ -159,16 +160,16 @@ fn test_system_prompt_separated_from_messages() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_empty_system_prompt_produces_empty_vec() {
|
fn test_empty_system_prompt_produces_empty_vec() {
|
||||||
let result =
|
let result =
|
||||||
build_converse_request(vec![], Some("".to_string()), vec![], 4096, None, None, None);
|
build_converse_request(vec![], Some("".to_string()), None, vec![], 4096, None, None, None);
|
||||||
assert!(result.system.is_empty());
|
assert!(result.system.is_empty());
|
||||||
|
|
||||||
let result2 = build_converse_request(vec![], None, vec![], 4096, None, None, None);
|
let result2 = build_converse_request(vec![], None, None, vec![], 4096, None, None, None);
|
||||||
assert!(result2.system.is_empty());
|
assert!(result2.system.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_tools_produce_none_config() {
|
fn test_empty_tools_produce_none_config() {
|
||||||
let result = build_converse_request(vec![], None, vec![], 4096, None, None, None);
|
let result = build_converse_request(vec![], None, None, vec![], 4096, None, None, None);
|
||||||
assert!(result.tool_config.is_none());
|
assert!(result.tool_config.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@ fn test_tool_definitions_produce_tool_config() {
|
|||||||
}),
|
}),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let result = build_converse_request(vec![], None, tools, 4096, None, None, None);
|
let result = build_converse_request(vec![], None, None, tools, 4096, None, None, None);
|
||||||
|
|
||||||
assert!(result.tool_config.is_some());
|
assert!(result.tool_config.is_some());
|
||||||
let config = result.tool_config.unwrap();
|
let config = result.tool_config.unwrap();
|
||||||
@@ -195,7 +196,7 @@ fn test_tool_definitions_produce_tool_config() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inference_config_max_tokens_only() {
|
fn test_inference_config_max_tokens_only() {
|
||||||
let result = build_converse_request(vec![], None, vec![], 8192, None, None, None);
|
let result = build_converse_request(vec![], None, None, vec![], 8192, None, None, None);
|
||||||
assert_eq!(result.inference_config.max_tokens(), Some(8192));
|
assert_eq!(result.inference_config.max_tokens(), Some(8192));
|
||||||
assert_eq!(result.inference_config.temperature(), None);
|
assert_eq!(result.inference_config.temperature(), None);
|
||||||
assert_eq!(result.inference_config.top_p(), None);
|
assert_eq!(result.inference_config.top_p(), None);
|
||||||
@@ -207,6 +208,7 @@ fn test_inference_config_all_params() {
|
|||||||
let result = build_converse_request(
|
let result = build_converse_request(
|
||||||
vec![],
|
vec![],
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
vec![],
|
vec![],
|
||||||
4096,
|
4096,
|
||||||
Some(0.7),
|
Some(0.7),
|
||||||
@@ -233,7 +235,7 @@ fn test_multipart_content_produces_multiple_blocks() {
|
|||||||
]),
|
]),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
assert_eq!(result.messages[0].content().len(), 2);
|
assert_eq!(result.messages[0].content().len(), 2);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@@ -271,7 +273,7 @@ fn test_tool_result_after_tool_use_coalesced_into_user_message() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let result = build_converse_request(messages, None, vec![], 4096, None, None, None);
|
let result = build_converse_request(messages, None, None, vec![], 4096, None, None, None);
|
||||||
|
|
||||||
assert_eq!(result.messages.len(), 3);
|
assert_eq!(result.messages.len(), 3);
|
||||||
assert_eq!(result.messages[0].role(), &ConversationRole::User);
|
assert_eq!(result.messages[0].role(), &ConversationRole::User);
|
||||||
|
|||||||
@@ -550,6 +550,7 @@ impl AgentSimulation {
|
|||||||
needs_create_task,
|
needs_create_task,
|
||||||
self.conversation.clone(),
|
self.conversation.clone(),
|
||||||
Some(self.system_prompt.clone()),
|
Some(self.system_prompt.clone()),
|
||||||
|
None,
|
||||||
self.tools.clone(),
|
self.tools.clone(),
|
||||||
8192,
|
8192,
|
||||||
None,
|
None,
|
||||||
@@ -557,6 +558,7 @@ impl AgentSimulation {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("converse_stream should succeed");
|
.expect("converse_stream should succeed");
|
||||||
@@ -1137,6 +1139,7 @@ async fn test_reasoning_model_produces_substantial_output() {
|
|||||||
true,
|
true,
|
||||||
messages,
|
messages,
|
||||||
system,
|
system,
|
||||||
|
None,
|
||||||
agent_tools(),
|
agent_tools(),
|
||||||
8192,
|
8192,
|
||||||
None,
|
None,
|
||||||
@@ -1144,6 +1147,7 @@ async fn test_reasoning_model_produces_substantial_output() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("converse_stream should succeed");
|
.expect("converse_stream should succeed");
|
||||||
@@ -1260,6 +1264,7 @@ async fn test_event_sequence_matches_controller_expectations() {
|
|||||||
true,
|
true,
|
||||||
messages,
|
messages,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
vec![],
|
vec![],
|
||||||
100,
|
100,
|
||||||
None,
|
None,
|
||||||
@@ -1267,6 +1272,7 @@ async fn test_event_sequence_matches_controller_expectations() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("should connect");
|
.expect("should connect");
|
||||||
@@ -1375,6 +1381,7 @@ async fn test_followup_turn_does_not_send_create_task() {
|
|||||||
false,
|
false,
|
||||||
messages,
|
messages,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
vec![],
|
vec![],
|
||||||
100,
|
100,
|
||||||
None,
|
None,
|
||||||
@@ -1382,6 +1389,7 @@ async fn test_followup_turn_does_not_send_create_task() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("should connect");
|
.expect("should connect");
|
||||||
@@ -1459,6 +1467,7 @@ async fn run_slash_command_test(
|
|||||||
true,
|
true,
|
||||||
messages,
|
messages,
|
||||||
system_prompt,
|
system_prompt,
|
||||||
|
None,
|
||||||
tools,
|
tools,
|
||||||
4096,
|
4096,
|
||||||
None,
|
None,
|
||||||
@@ -1466,6 +1475,7 @@ async fn run_slash_command_test(
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("stream should connect");
|
.expect("stream should connect");
|
||||||
@@ -1687,6 +1697,7 @@ async fn test_slash_resume_conversation() {
|
|||||||
true,
|
true,
|
||||||
messages,
|
messages,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
vec![],
|
vec![],
|
||||||
256,
|
256,
|
||||||
None,
|
None,
|
||||||
@@ -1694,6 +1705,7 @@ async fn test_slash_resume_conversation() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("stream should connect");
|
.expect("stream should connect");
|
||||||
@@ -1799,6 +1811,7 @@ async fn test_empty_messages_safety_check() {
|
|||||||
true,
|
true,
|
||||||
messages,
|
messages,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
vec![],
|
vec![],
|
||||||
100,
|
100,
|
||||||
None,
|
None,
|
||||||
@@ -1806,6 +1819,7 @@ async fn test_empty_messages_safety_check() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("safety fallback message should work");
|
.expect("safety fallback message should work");
|
||||||
@@ -1960,6 +1974,7 @@ async fn test_full_proto_round_trip_with_tool_history() {
|
|||||||
false,
|
false,
|
||||||
messages,
|
messages,
|
||||||
system_prompt,
|
system_prompt,
|
||||||
|
None,
|
||||||
tools,
|
tools,
|
||||||
1024,
|
1024,
|
||||||
None,
|
None,
|
||||||
@@ -1967,6 +1982,7 @@ async fn test_full_proto_round_trip_with_tool_history() {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ async fn collect_stream_output(
|
|||||||
true,
|
true,
|
||||||
messages,
|
messages,
|
||||||
system_prompt,
|
system_prompt,
|
||||||
|
None,
|
||||||
tools,
|
tools,
|
||||||
8192,
|
8192,
|
||||||
None,
|
None,
|
||||||
@@ -66,6 +67,7 @@ async fn collect_stream_output(
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(Mutex::new(Vec::new())),
|
Arc::new(Mutex::new(Vec::new())),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("converse_stream should succeed");
|
.expect("converse_stream should succeed");
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub struct TranslatorRequest {
|
|||||||
pub model_id: String,
|
pub model_id: String,
|
||||||
pub root_task_id: Option<String>,
|
pub root_task_id: Option<String>,
|
||||||
pub bedrock_message_history: Vec<ConversationMessage>,
|
pub bedrock_message_history: Vec<ConversationMessage>,
|
||||||
|
pub bedrock_compact_summary: Option<String>,
|
||||||
pub bedrock_messages_sent: Arc<Mutex<Vec<ConversationMessage>>>,
|
pub bedrock_messages_sent: Arc<Mutex<Vec<ConversationMessage>>>,
|
||||||
pub is_summarization: bool,
|
pub is_summarization: bool,
|
||||||
}
|
}
|
||||||
@@ -71,12 +72,14 @@ pub async fn execute(
|
|||||||
request_translator::sanitize_messages_for_bedrock(&mut messages);
|
request_translator::sanitize_messages_for_bedrock(&mut messages);
|
||||||
|
|
||||||
let system_prompt = request_translator::extract_system_prompt(request);
|
let system_prompt = request_translator::extract_system_prompt(request);
|
||||||
|
let compact_summary = params.bedrock_compact_summary;
|
||||||
let tools = request_translator::extract_tools(request);
|
let tools = request_translator::extract_tools(request);
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"[bedrock] Sending {} messages, system_prompt={}, tools={}",
|
"[bedrock] Sending {} messages, system_prompt={}, compact_summary={}, tools={}",
|
||||||
messages.len(),
|
messages.len(),
|
||||||
system_prompt.is_some(),
|
system_prompt.is_some(),
|
||||||
|
compact_summary.is_some(),
|
||||||
tools.len()
|
tools.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -94,6 +97,7 @@ pub async fn execute(
|
|||||||
needs_create_task,
|
needs_create_task,
|
||||||
messages.clone(),
|
messages.clone(),
|
||||||
system_prompt,
|
system_prompt,
|
||||||
|
compact_summary,
|
||||||
tools,
|
tools,
|
||||||
64000,
|
64000,
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -1963,6 +1963,7 @@ impl BlocklistAIController {
|
|||||||
parent_agent_id,
|
parent_agent_id,
|
||||||
agent_name,
|
agent_name,
|
||||||
bedrock_history,
|
bedrock_history,
|
||||||
|
bedrock_compact_summary,
|
||||||
) = {
|
) = {
|
||||||
let Some(conversation) = history_model
|
let Some(conversation) = history_model
|
||||||
.as_ref(ctx)
|
.as_ref(ctx)
|
||||||
@@ -1986,6 +1987,7 @@ impl BlocklistAIController {
|
|||||||
conversation.parent_agent_id().map(str::to_string),
|
conversation.parent_agent_id().map(str::to_string),
|
||||||
conversation.agent_name().map(str::to_string),
|
conversation.agent_name().map(str::to_string),
|
||||||
conversation.bedrock_message_history().to_vec(),
|
conversation.bedrock_message_history().to_vec(),
|
||||||
|
conversation.compact_summary().map(str::to_string),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2053,6 +2055,7 @@ impl BlocklistAIController {
|
|||||||
request_params.parent_agent_id = parent_agent_id;
|
request_params.parent_agent_id = parent_agent_id;
|
||||||
request_params.agent_name = agent_name;
|
request_params.agent_name = agent_name;
|
||||||
request_params.bedrock_message_history = bedrock_history;
|
request_params.bedrock_message_history = bedrock_history;
|
||||||
|
request_params.bedrock_compact_summary = bedrock_compact_summary;
|
||||||
request_params.is_summarization = request_input
|
request_params.is_summarization = request_input
|
||||||
.all_inputs()
|
.all_inputs()
|
||||||
.any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. }));
|
.any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. }));
|
||||||
@@ -2415,37 +2418,14 @@ impl BlocklistAIController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(summary) = summary_text {
|
if let Some(summary) = summary_text {
|
||||||
use crate::ai::bedrock::convert::{
|
|
||||||
ConversationMessage, MessageContent,
|
|
||||||
MessageRole,
|
|
||||||
};
|
|
||||||
let assistant_reply = "Understood. I have the context from our previous conversation. How can I help you next?";
|
|
||||||
let user_msg = format!(
|
|
||||||
"Here is a summary of our conversation so far:\n\n{summary}"
|
|
||||||
);
|
|
||||||
let compacted = vec![
|
|
||||||
ConversationMessage {
|
|
||||||
role: MessageRole::User,
|
|
||||||
content: MessageContent::Text(user_msg.clone()),
|
|
||||||
},
|
|
||||||
ConversationMessage {
|
|
||||||
role: MessageRole::Assistant,
|
|
||||||
content: MessageContent::Text(
|
|
||||||
assistant_reply.to_string()
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"[bedrock] Compacted conversation history from {} messages to {} (summary)",
|
"[bedrock] Compacted conversation history from {} messages to system-level summary",
|
||||||
new_history.len(),
|
new_history.len()
|
||||||
compacted.len()
|
|
||||||
);
|
);
|
||||||
*conversation.bedrock_message_history_mut() =
|
conversation.set_compact_summary(Some(summary.clone()));
|
||||||
compacted;
|
*conversation.bedrock_message_history_mut() = Vec::new();
|
||||||
|
|
||||||
// Estimate new context size from the compacted content.
|
let estimated_tokens = (summary.len() / 4) as u32;
|
||||||
// ~4 chars per token is a reasonable approximation.
|
|
||||||
let estimated_tokens = ((user_msg.len() + assistant_reply.len()) / 4) as u32;
|
|
||||||
let max_context = crate::ai::bedrock::response_translator::context_window_for_model("claude-opus-4-6-20250514[1m]");
|
let max_context = crate::ai::bedrock::response_translator::context_window_for_model("claude-opus-4-6-20250514[1m]");
|
||||||
let new_usage = estimated_tokens as f32 / max_context as f32;
|
let new_usage = estimated_tokens as f32 / max_context as f32;
|
||||||
conversation.set_context_window_usage(new_usage);
|
conversation.set_context_window_usage(new_usage);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ mod tools;
|
|||||||
pub use context::PromptContext;
|
pub use context::PromptContext;
|
||||||
pub use mode::Mode;
|
pub use mode::Mode;
|
||||||
pub use prompts::provider::Provider;
|
pub use prompts::provider::Provider;
|
||||||
pub use tools::ToolSet;
|
pub use tools::tools_for_mode;
|
||||||
|
|
||||||
use crate::ai::bedrock::convert::ToolDefinition;
|
use crate::ai::bedrock::convert::ToolDefinition;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user