compactの実装
This commit is contained in:
@@ -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};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -31,6 +31,10 @@ impl MockLlmClient {
|
||||
|
||||
#[async_trait]
|
||||
impl LlmClient for MockLlmClient {
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
async fn stream(
|
||||
&self,
|
||||
_request: Request,
|
||||
|
||||
@@ -15,6 +15,8 @@ async fn round_trip_write_and_read() {
|
||||
system_prompt: Some("You are helpful.".into()),
|
||||
config: RequestConfig::default().with_max_tokens(1024),
|
||||
history: vec![],
|
||||
forked_from: None,
|
||||
compacted_from: None,
|
||||
},
|
||||
LogEntry::UserInput {
|
||||
ts: 2000,
|
||||
@@ -72,6 +74,8 @@ async fn create_session_writes_all_entries() {
|
||||
system_prompt: None,
|
||||
config: RequestConfig::default(),
|
||||
history: vec![Item::user_message("seed"), Item::assistant_message("ok")],
|
||||
forked_from: None,
|
||||
compacted_from: None,
|
||||
}]);
|
||||
|
||||
store.create_session(id, &entries).await.unwrap();
|
||||
@@ -97,12 +101,16 @@ async fn list_sessions_returns_newest_first() {
|
||||
system_prompt: None,
|
||||
config: RequestConfig::default(),
|
||||
history: vec![],
|
||||
forked_from: None,
|
||||
compacted_from: None,
|
||||
}]);
|
||||
let entries2 = build_chain(&[LogEntry::SessionStart {
|
||||
ts: 1001,
|
||||
system_prompt: None,
|
||||
config: RequestConfig::default(),
|
||||
history: vec![],
|
||||
forked_from: None,
|
||||
compacted_from: None,
|
||||
}]);
|
||||
|
||||
store.append(id1, &entries1[0]).await.unwrap();
|
||||
@@ -127,6 +135,8 @@ async fn exists_returns_correct_state() {
|
||||
system_prompt: None,
|
||||
config: RequestConfig::default(),
|
||||
history: vec![],
|
||||
forked_from: None,
|
||||
compacted_from: None,
|
||||
}]);
|
||||
store.append(id, &entries[0]).await.unwrap();
|
||||
|
||||
@@ -155,6 +165,8 @@ async fn trace_entries_in_separate_file() {
|
||||
system_prompt: None,
|
||||
config: RequestConfig::default(),
|
||||
history: vec![],
|
||||
forked_from: None,
|
||||
compacted_from: None,
|
||||
}]);
|
||||
store.append(id, &entries[0]).await.unwrap();
|
||||
|
||||
@@ -189,6 +201,8 @@ async fn read_head_hash_returns_last_entry_hash() {
|
||||
system_prompt: None,
|
||||
config: RequestConfig::default(),
|
||||
history: vec![],
|
||||
forked_from: None,
|
||||
compacted_from: None,
|
||||
},
|
||||
LogEntry::UserInput {
|
||||
ts: 2000,
|
||||
|
||||
Reference in New Issue
Block a user