huskies: merge 828

This commit is contained in:
dave
2026-04-29 00:24:47 +00:00
parent 6f30815b64
commit f3bb0a6f4b
+98 -1
View File
@@ -7,6 +7,7 @@ use crate::llm::types::{Message, Role, ToolDefinition};
use crate::slog; use crate::slog;
use crate::state::SessionState; use crate::state::SessionState;
use crate::store::StoreOps; use crate::store::StoreOps;
use chrono::Utc;
use serde::Deserialize; use serde::Deserialize;
const MAX_TURNS: usize = 30; const MAX_TURNS: usize = 30;
@@ -32,6 +33,17 @@ pub struct ChatResult {
pub session_id: Option<String>, pub session_id: Option<String>,
} }
/// Prepend an ISO-8601 UTC timestamp to the content of the last user message.
///
/// Only the most-recent user message is annotated so that prior turns in the
/// conversation history are not re-stamped on every call. Assistant, system,
/// and tool messages are left untouched.
fn inject_received_at(messages: &mut [Message], ts: &str) {
if let Some(msg) = messages.iter_mut().rev().find(|m| m.role == Role::User) {
msg.content = format!("[{ts}] {}", msg.content);
}
}
fn get_anthropic_api_key_impl(store: &dyn StoreOps) -> Result<String, String> { fn get_anthropic_api_key_impl(store: &dyn StoreOps) -> Result<String, String> {
match store.get(KEY_ANTHROPIC_API_KEY) { match store.get(KEY_ANTHROPIC_API_KEY) {
Some(value) => { Some(value) => {
@@ -103,7 +115,7 @@ pub fn cancel_chat(state: &SessionState) -> Result<(), String> {
/// Run a multi-turn chat with tool calling against the configured provider. /// Run a multi-turn chat with tool calling against the configured provider.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn chat<F, U, T, A>( pub async fn chat<F, U, T, A>(
messages: Vec<Message>, mut messages: Vec<Message>,
config: ProviderConfig, config: ProviderConfig,
state: &SessionState, state: &SessionState,
store: &dyn StoreOps, store: &dyn StoreOps,
@@ -121,6 +133,12 @@ where
use crate::llm::providers::anthropic::AnthropicProvider; use crate::llm::providers::anthropic::AnthropicProvider;
use crate::llm::providers::ollama::OllamaProvider; use crate::llm::providers::ollama::OllamaProvider;
// Stamp the current user message with the wall-clock time at the transport
// boundary (i.e. when this function is entered, which is immediately after
// the WebSocket frame is received — before any LLM invocation).
let received_at = Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string();
inject_received_at(&mut messages, &received_at);
let _ = state.cancel_tx.send(false); let _ = state.cancel_tx.send(false);
let mut cancel_rx = state.cancel_rx.clone(); let mut cancel_rx = state.cancel_rx.clone();
cancel_rx.borrow_and_update(); cancel_rx.borrow_and_update();
@@ -474,6 +492,85 @@ mod tests {
} }
} }
// ---------------------------------------------------------------------------
// inject_received_at
// ---------------------------------------------------------------------------
#[test]
fn inject_timestamp_into_last_user_message() {
let mut messages = vec![Message {
role: Role::User,
content: "hello".to_string(),
tool_calls: None,
tool_call_id: None,
}];
inject_received_at(&mut messages, "2026-04-28T10:30:00Z");
assert_eq!(messages[0].content, "[2026-04-28T10:30:00Z] hello");
}
#[test]
fn inject_timestamp_only_last_user_message() {
let mut messages = vec![
Message {
role: Role::User,
content: "first".to_string(),
tool_calls: None,
tool_call_id: None,
},
Message {
role: Role::Assistant,
content: "reply".to_string(),
tool_calls: None,
tool_call_id: None,
},
Message {
role: Role::User,
content: "second".to_string(),
tool_calls: None,
tool_call_id: None,
},
];
inject_received_at(&mut messages, "2026-04-28T10:30:00Z");
// Only the last user message is stamped.
assert_eq!(messages[0].content, "first");
assert_eq!(messages[1].content, "reply");
assert_eq!(messages[2].content, "[2026-04-28T10:30:00Z] second");
}
#[test]
fn inject_timestamp_skips_assistant_messages() {
let mut messages = vec![Message {
role: Role::Assistant,
content: "bot reply".to_string(),
tool_calls: None,
tool_call_id: None,
}];
inject_received_at(&mut messages, "2026-04-28T10:30:00Z");
// No user message — nothing changes.
assert_eq!(messages[0].content, "bot reply");
}
#[test]
fn inject_timestamp_does_not_stamp_system_messages() {
let mut messages = vec![
Message {
role: Role::System,
content: "sys".to_string(),
tool_calls: None,
tool_call_id: None,
},
Message {
role: Role::User,
content: "hello".to_string(),
tool_calls: None,
tool_call_id: None,
},
];
inject_received_at(&mut messages, "2026-04-28T10:30:00Z");
assert_eq!(messages[0].content, "sys");
assert_eq!(messages[1].content, "[2026-04-28T10:30:00Z] hello");
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// cancel_chat // cancel_chat
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------