compactの実装

This commit is contained in:
2026-04-12 07:09:48 +09:00
parent 47c59a416e
commit bcc7faa0ba
19 changed files with 439 additions and 21 deletions
+5 -4
View File
@@ -35,12 +35,13 @@ pub mod store;
pub use event_trace::TraceEntry;
pub use fs_store::FsStore;
pub use session::{
SessionStartState, create_session, ensure_head_or_fork, fork, fork_at, restore, save_cache_locked,
save_cache_unlocked, save_config_changed, save_delta, save_outcome, save_turn_end,
SessionStartState, create_compacted_session, create_session, ensure_head_or_fork, fork, fork_at,
restore, save_cache_locked, save_cache_unlocked, save_config_changed, save_delta, save_outcome,
save_turn_end,
};
pub use session_log::{
EntryHash, HashedEntry, LogEntry, Outcome, RestoredState, build_chain, collect_state,
compute_hash,
EntryHash, HashedEntry, LogEntry, Outcome, RestoredState, SessionOrigin, build_chain,
collect_state, compute_hash,
};
pub use store::{Store, StoreError};
+44 -1
View File
@@ -4,7 +4,7 @@
//! The caller (typically Pod) holds the Worker directly and calls these
//! functions after state-mutating operations.
use crate::session_log::{self, EntryHash, HashedEntry, LogEntry, Outcome};
use crate::session_log::{self, EntryHash, HashedEntry, LogEntry, Outcome, SessionOrigin};
use crate::store::{Store, StoreError};
use crate::SessionId;
use llm_worker::llm_client::types::Item;
@@ -30,6 +30,40 @@ pub async fn create_session(
system_prompt: state.system_prompt.map(String::from),
config: state.config.clone(),
history: state.history.to_vec(),
forked_from: None,
compacted_from: None,
};
let hash = session_log::compute_hash(None, &entry);
let hashed_entry = HashedEntry {
hash: hash.clone(),
prev_hash: None,
entry,
};
store.append(session_id, &hashed_entry).await?;
Ok((session_id, hash))
}
/// Create a compacted session from an existing one.
///
/// Records `compacted_from` provenance linking back to the source session.
/// Returns the new session ID and head hash.
pub async fn create_compacted_session(
store: &impl Store,
state: SessionStartState<'_>,
source_session_id: SessionId,
source_head_hash: EntryHash,
) -> Result<(SessionId, EntryHash), StoreError> {
let session_id = crate::new_session_id();
let entry = LogEntry::SessionStart {
ts: session_log::now_millis(),
system_prompt: state.system_prompt.map(String::from),
config: state.config.clone(),
history: state.history.to_vec(),
forked_from: None,
compacted_from: Some(SessionOrigin {
session_id: source_session_id,
at_hash: source_head_hash,
}),
};
let hash = session_log::compute_hash(None, &entry);
let hashed_entry = HashedEntry {
@@ -73,6 +107,8 @@ pub async fn ensure_head_or_fork(
system_prompt: state.system_prompt.map(String::from),
config: state.config.clone(),
history: state.history.to_vec(),
forked_from: None,
compacted_from: None,
};
let hash = session_log::compute_hash(None, &entry);
let hashed_entry = HashedEntry {
@@ -229,6 +265,8 @@ pub async fn fork(
system_prompt: state.system_prompt.map(String::from),
config: state.config.clone(),
history: state.history.to_vec(),
forked_from: None,
compacted_from: None,
};
let hash = session_log::compute_hash(None, &entry);
let hashed_entry = HashedEntry {
@@ -260,6 +298,11 @@ pub async fn fork_at(
system_prompt: state.system_prompt,
config: state.config,
history: state.history,
forked_from: Some(session_log::SessionOrigin {
session_id: source_id,
at_hash: at_hash.clone(),
}),
compacted_from: None,
};
let hash = session_log::compute_hash(None, &entry);
let hashed_entry = HashedEntry {
+29
View File
@@ -101,6 +101,12 @@ pub enum LogEntry {
system_prompt: Option<String>,
config: RequestConfig,
history: Vec<Item>,
/// Origin: forked from another session at a specific entry.
#[serde(default, skip_serializing_if = "Option::is_none")]
forked_from: Option<SessionOrigin>,
/// Origin: compacted from another session at a specific entry.
#[serde(default, skip_serializing_if = "Option::is_none")]
compacted_from: Option<SessionOrigin>,
},
/// User input pushed to history (worker.rs:229).
@@ -137,6 +143,15 @@ pub enum LogEntry {
ConfigChanged { ts: u64, config: RequestConfig },
}
/// Provenance reference to a parent session.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SessionOrigin {
/// Session ID of the source session.
pub session_id: crate::SessionId,
/// Hash of the entry in the source session at the point of fork/compact.
pub at_hash: EntryHash,
}
/// Outcome of a run/resume call. Metadata for auditing only.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
@@ -269,6 +284,8 @@ mod tests {
system_prompt: Some("You are helpful.".into()),
config: RequestConfig::default().with_max_tokens(1024),
history: vec![Item::user_message("seed")],
forked_from: None,
compacted_from: None,
}]);
let state = collect_state(&entries);
assert_eq!(state.system_prompt.as_deref(), Some("You are helpful."));
@@ -285,6 +302,8 @@ mod tests {
system_prompt: None,
config: RequestConfig::default(),
history: vec![],
forked_from: None,
compacted_from: None,
},
LogEntry::UserInput {
ts: 2000,
@@ -318,6 +337,8 @@ mod tests {
system_prompt: None,
config: RequestConfig::default(),
history: vec![],
forked_from: None,
compacted_from: None,
},
LogEntry::UserInput {
ts: 2000,
@@ -354,6 +375,8 @@ mod tests {
system_prompt: None,
config: RequestConfig::default(),
history: vec![Item::user_message("a"), Item::assistant_message("b")],
forked_from: None,
compacted_from: None,
},
LogEntry::Locked {
ts: 2000,
@@ -377,6 +400,8 @@ mod tests {
system_prompt: None,
config: RequestConfig::default(),
history: vec![],
forked_from: None,
compacted_from: None,
},
LogEntry::ConfigChanged {
ts: 2000,
@@ -395,6 +420,8 @@ mod tests {
system_prompt: None,
config: RequestConfig::default(),
history: vec![],
forked_from: None,
compacted_from: None,
},
LogEntry::UserInput {
ts: 2000,
@@ -429,6 +456,8 @@ mod tests {
system_prompt: None,
config: RequestConfig::default(),
history: vec![],
forked_from: None,
compacted_from: None,
};
let hash = compute_hash(None, &entry);
let hex = hash.to_hex();