diff --git a/Cargo.lock b/Cargo.lock index 82e71cf7..d867601e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2331,6 +2331,7 @@ dependencies = [ "gemini_client_rs", "indexmap", "infer", + "insta", "jp_attachment", "jp_config", "jp_conversation", diff --git a/crates/jp_llm/Cargo.toml b/crates/jp_llm/Cargo.toml index efad9cdc..fc371a4a 100644 --- a/crates/jp_llm/Cargo.toml +++ b/crates/jp_llm/Cargo.toml @@ -55,6 +55,7 @@ assert_matches = { workspace = true } tokio = { workspace = true, features = ["test-util"] } eventsource-stream = { workspace = true } infer = { workspace = true, features = ["std"] } +insta = { workspace = true, features = ["json"] } jp_test = { workspace = true } paste = { workspace = true } test-log = { workspace = true } diff --git a/crates/jp_llm/src/provider.rs b/crates/jp_llm/src/provider.rs index 9769ff39..96ca09e2 100644 --- a/crates/jp_llm/src/provider.rs +++ b/crates/jp_llm/src/provider.rs @@ -65,6 +65,39 @@ pub fn get_provider(id: ProviderId, config: &LlmProviderConfig) -> Result Result { + match id { + ProviderId::Anthropic => { + Anthropic::try_from(&config.anthropic)?.request_value(model, query) + } + ProviderId::Cerebras => Cerebras::try_from(&config.cerebras)?.request_value(model, query), + ProviderId::Google => Google::try_from(&config.google)?.request_value(model, query), + ProviderId::Llamacpp => Llamacpp::try_from(&config.llamacpp)?.request_value(model, query), + ProviderId::Ollama => Ollama::try_from(&config.ollama)?.request_value(model, query), + ProviderId::Openai => Openai::try_from(&config.openai)?.request_value(model, query), + ProviderId::Openrouter => { + Openrouter::try_from(&config.openrouter)?.request_value(model, query) + } + ProviderId::Test | ProviderId::Deepseek | ProviderId::Xai => { + unreachable!("{id:?} is not part of the request snapshot suite") + } + } +} + /// Serialize a value to a temporary JSON file and return its path as a string. /// /// Used by `trace!` fields to avoid dumping massive request payloads into the @@ -89,3 +122,7 @@ pub(crate) fn trace_to_tmpfile(prefix: &str, value: &impl serde::Serialize) -> S #[cfg(test)] #[path = "provider_tests.rs"] mod tests; + +#[cfg(test)] +#[path = "provider/compaction_request_tests.rs"] +mod compaction_request_tests; diff --git a/crates/jp_llm/src/provider/anthropic.rs b/crates/jp_llm/src/provider/anthropic.rs index fc39eb6e..58c45ee4 100644 --- a/crates/jp_llm/src/provider/anthropic.rs +++ b/crates/jp_llm/src/provider/anthropic.rs @@ -900,6 +900,22 @@ fn resolve_cache_control(policy: CachePolicy) -> Option { }) } +#[cfg(test)] +impl Anthropic { + /// Build the Anthropic wire request for `query` and serialize it to JSON + /// without sending. + /// Test-only seam for snapshotting request construction (notably compaction + /// projection) across providers. + pub(crate) fn request_value( + &self, + model: &ModelDetails, + query: ChatQuery, + ) -> Result { + let (request, ..) = create_request(model, query, true, &self.beta)?; + Ok(serde_json::to_value(request)?) + } +} + #[expect(clippy::too_many_lines)] fn create_request( model: &ModelDetails, diff --git a/crates/jp_llm/src/provider/cerebras.rs b/crates/jp_llm/src/provider/cerebras.rs index 524fbadc..ced80d4d 100644 --- a/crates/jp_llm/src/provider/cerebras.rs +++ b/crates/jp_llm/src/provider/cerebras.rs @@ -94,7 +94,7 @@ impl Provider for Cerebras { "Starting Cerebras chat completion stream." ); - let (body, is_structured) = build_request(model, query)?; + let (body, is_structured) = create_request(model, query)?; debug!(stream = true, "Cerebras chat completion stream request."); trace!( @@ -258,7 +258,27 @@ fn map_model(id: &str) -> Result { Ok(details) } -fn build_request(model: &ModelDetails, query: ChatQuery) -> Result<(Value, bool)> { +#[cfg(test)] +impl Cerebras { + /// Build the Cerebras wire request for `query` and serialize it to JSON + /// without sending. + /// Test-only seam for snapshotting request construction (notably compaction + /// projection) across providers. + #[expect( + clippy::unused_self, + reason = "uniform per-provider seam; only some providers read instance state" + )] + pub(crate) fn request_value( + &self, + model: &ModelDetails, + query: ChatQuery, + ) -> Result { + let (request, _) = create_request(model, query)?; + Ok(request) + } +} + +fn create_request(model: &ModelDetails, query: ChatQuery) -> Result<(Value, bool)> { let ChatQuery { thread, tools, diff --git a/crates/jp_llm/src/provider/compaction_request_tests.rs b/crates/jp_llm/src/provider/compaction_request_tests.rs new file mode 100644 index 00000000..2746db9c --- /dev/null +++ b/crates/jp_llm/src/provider/compaction_request_tests.rs @@ -0,0 +1,266 @@ +//! Per-provider wire-request snapshots for conversation compaction. +//! +//! Builds a conversation stream with compaction overlays, runs each provider's +//! real request builder (no network, no recorded responses), and snapshots the +//! serialized request that would go on the wire. +//! A scenario is a single provider-parameterized function; +//! `request_for_all_providers!` fans it out across every provider, mirroring +//! the `test_all_providers!` pattern in `provider_tests.rs`. +//! +//! Every scenario shares one base conversation (`base_stream`) and differs only +//! in the compaction overlay it appends, so the snapshots isolate each policy's +//! effect on the request. + +use chrono::{DateTime, Duration, TimeZone as _, Utc}; +use jp_config::{ + AppConfig, + model::{ + id::{ModelIdConfig, ModelIdOrAliasConfig, ProviderId}, + parameters::ReasoningConfig, + }, + providers::llm::LlmProviderConfig, +}; +use jp_conversation::{ + Compaction, ConversationStream, ReasoningPolicy, SummaryPolicy, ToolCallPolicy, + event::{ + ChatRequest, ChatResponse, ConversationEvent, ToolCallRequest, ToolCallResponse, TurnStart, + }, + thread::ThreadBuilder, +}; +use jp_test::Result; + +use super::build_request_value; +use crate::{query::ChatQuery, test::test_model_details}; + +/// Fixed timestamp for every event and overlay, so snapshots are deterministic. +fn ts() -> DateTime { + Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap() +} + +/// Provider config whose API-key env vars point at a variable present in the +/// test environment, so each provider constructs offline without real +/// credentials. +/// Mirrors the dummy-key handling in the VCR harness. +fn provider_config() -> LlmProviderConfig { + let env = if cfg!(windows) { "USERNAME" } else { "USER" }.to_owned(); + let mut config = LlmProviderConfig::default(); + config.anthropic.api_key_env = env.clone(); + config.cerebras.api_key_env = env.clone(); + config.google.api_key_env = env.clone(); + config.openai.api_key_env = env.clone(); + config.openrouter.api_key_env = env; + config +} + +/// Deterministic base config: reasoning off at the model level (the +/// conversation reasoning *events* are what compaction strips, independent of +/// this), model pinned to the provider under test. +fn base_config(provider: ProviderId) -> AppConfig { + let mut config = AppConfig::new_test(); + config.assistant.model.parameters.reasoning = Some(ReasoningConfig::Off); + config.assistant.model.id = ModelIdOrAliasConfig::Id(ModelIdConfig { + provider, + name: "test".parse().unwrap(), + }); + config +} + +/// A four-turn conversation exercising every content type a compaction policy +/// can touch: +/// +/// - turn 0: a reasoning event, +/// - turn 1: a tool call request/response pair, +/// - turn 2: a plain message exchange, +/// - turn 3: a trailing pending request (outside every scenario's range). +fn base_stream(provider: ProviderId) -> ConversationStream { + let ts = ts(); + let mut stream = ConversationStream::new(base_config(provider).into()).with_created_at(ts); + + let mut tool_args = serde_json::Map::new(); + tool_args.insert("path".into(), "notes.md".into()); + + stream.extend([ + ConversationEvent::new(TurnStart, ts), + ConversationEvent::new(ChatRequest::from("What is the capital of France?"), ts), + ConversationEvent::new( + ChatResponse::reasoning("The user wants a capital city."), + ts, + ), + ConversationEvent::new(ChatResponse::message("Paris."), ts), + ConversationEvent::new(TurnStart, ts), + ConversationEvent::new(ChatRequest::from("Read my notes file."), ts), + ConversationEvent::new( + ToolCallRequest::new("call_1".to_owned(), "read_file".to_owned(), tool_args), + ts, + ), + ConversationEvent::new( + ToolCallResponse { + id: "call_1".to_owned(), + result: Ok("- buy milk\n- call dentist".to_owned()), + }, + ts, + ), + ConversationEvent::new( + ChatResponse::message("Your notes mention milk and the dentist."), + ts, + ), + ConversationEvent::new(TurnStart, ts), + ConversationEvent::new(ChatRequest::from("And the capital of Germany?"), ts), + ConversationEvent::new(ChatResponse::message("Berlin."), ts), + ConversationEvent::new(TurnStart, ts), + ConversationEvent::new(ChatRequest::from("And of Italy?"), ts), + ]); + + stream +} + +/// Apply a compaction overlay to the base stream, build the provider request, +/// and snapshot it at `tests/fixtures//compaction/.snap`. +fn snapshot( + provider: ProviderId, + name: &str, + compact: impl FnOnce(&mut ConversationStream), +) -> Result { + let mut stream = base_stream(provider); + compact(&mut stream); + + let thread = ThreadBuilder::new().with_events(stream).build().unwrap(); + let request = build_request_value( + provider, + &provider_config(), + &test_model_details(provider), + ChatQuery::from(thread), + )?; + + let path = format!( + "{}/tests/fixtures/{}/compaction", + env!("CARGO_MANIFEST_DIR"), + provider.as_str(), + ); + + insta::with_settings!({ snapshot_path => path, prepend_module_to_snapshot => false }, { + insta::assert_json_snapshot!(name, request); + }); + + Ok(()) +} + +/// No compaction: the full conversation, as a control for every other scenario. +fn baseline(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |_| {}) +} + +/// Reasoning events in the compacted range are dropped from the request. +fn reasoning_strip(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |stream| { + stream.add_compaction(Compaction::new(0, 0).with_reasoning(ReasoningPolicy::Strip)); + }) +} + +/// The compacted range collapses to a single synthetic request/response pair +/// carrying a pre-computed summary; the trailing turn survives. +fn summary(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |stream| { + stream.add_compaction( + Compaction::new(0, 2).with_summary(SummaryPolicy { + summary: "Earlier: the user asked about France's capital and had their notes read." + .to_owned(), + }), + ); + }) +} + +/// Tool call request arguments in the compacted range are blanked to `{}`; the +/// response is untouched. +fn tool_strip_request(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |stream| { + stream.add_compaction( + Compaction::new(1, 1).with_tool_calls(ToolCallPolicy::Strip { + request: true, + response: false, + }), + ); + }) +} + +/// Tool call responses in the compacted range are replaced with a status line; +/// the request is untouched. +fn tool_strip_response(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |stream| { + stream.add_compaction( + Compaction::new(1, 1).with_tool_calls(ToolCallPolicy::Strip { + request: false, + response: true, + }), + ); + }) +} + +/// Both the request arguments and the response content are stripped. +fn tool_strip_both(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |stream| { + stream.add_compaction( + Compaction::new(1, 1).with_tool_calls(ToolCallPolicy::Strip { + request: true, + response: true, + }), + ); + }) +} + +/// The tool call request/response pair is removed entirely from the request. +fn tool_omit(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |stream| { + stream.add_compaction(Compaction::new(1, 1).with_tool_calls(ToolCallPolicy::Omit)); + }) +} + +/// Two summaries with overlapping ranges: the later-timestamped one wins the +/// shared turn, so turn 0 keeps summary A and turns 1-2 resolve to summary B. +fn summary_overlap(provider: ProviderId, name: &str) -> Result { + snapshot(provider, name, |stream| { + let mut a = Compaction::new(0, 1).with_summary(SummaryPolicy { + summary: "Summary A: France's capital and the start of the notes lookup.".to_owned(), + }); + a.timestamp = ts(); + + let mut b = Compaction::new(1, 2).with_summary(SummaryPolicy { + summary: "Summary B: the notes lookup and Germany's capital.".to_owned(), + }); + b.timestamp = ts() + Duration::seconds(1); + + stream.add_compaction(a); + stream.add_compaction(b); + }) +} + +macro_rules! request_for_all_providers { + ($($scenario:ident),* $(,)?) => { + mod anthropic { use super::*; $(request_for_all_providers!(@case $scenario, ProviderId::Anthropic);)* } + mod cerebras { use super::*; $(request_for_all_providers!(@case $scenario, ProviderId::Cerebras);)* } + mod google { use super::*; $(request_for_all_providers!(@case $scenario, ProviderId::Google);)* } + mod llamacpp { use super::*; $(request_for_all_providers!(@case $scenario, ProviderId::Llamacpp);)* } + mod ollama { use super::*; $(request_for_all_providers!(@case $scenario, ProviderId::Ollama);)* } + mod openai { use super::*; $(request_for_all_providers!(@case $scenario, ProviderId::Openai);)* } + mod openrouter { use super::*; $(request_for_all_providers!(@case $scenario, ProviderId::Openrouter);)* } + }; + (@case $scenario:ident, $provider:expr) => { + paste::paste! { + #[test] + fn [< test_ $scenario >]() -> Result { + $scenario($provider, stringify!($scenario)) + } + } + }; +} + +request_for_all_providers![ + baseline, + reasoning_strip, + summary, + tool_strip_request, + tool_strip_response, + tool_strip_both, + tool_omit, + summary_overlap, +]; diff --git a/crates/jp_llm/src/provider/google.rs b/crates/jp_llm/src/provider/google.rs index 57b45fc1..0a457eb5 100644 --- a/crates/jp_llm/src/provider/google.rs +++ b/crates/jp_llm/src/provider/google.rs @@ -150,6 +150,26 @@ fn call( }) } +#[cfg(test)] +impl Google { + /// Build the Google wire request for `query` and serialize it to JSON + /// without sending. + /// Test-only seam for snapshotting request construction (notably compaction + /// projection) across providers. + #[expect( + clippy::unused_self, + reason = "uniform per-provider seam; only some providers read instance state" + )] + pub(crate) fn request_value( + &self, + model: &ModelDetails, + query: ChatQuery, + ) -> Result { + let (request, _) = create_request(model, query)?; + Ok(serde_json::to_value(request)?) + } +} + #[expect(clippy::too_many_lines)] fn create_request( model: &ModelDetails, diff --git a/crates/jp_llm/src/provider/llamacpp.rs b/crates/jp_llm/src/provider/llamacpp.rs index ef1a6967..467715a8 100644 --- a/crates/jp_llm/src/provider/llamacpp.rs +++ b/crates/jp_llm/src/provider/llamacpp.rs @@ -87,7 +87,7 @@ impl Provider for Llamacpp { "Starting Llamacpp chat completion stream." ); - let (body, is_structured) = build_request(model, query)?; + let (body, is_structured) = create_request(model, query)?; trace!( body = serde_json::to_string(&body).unwrap_or_default(), @@ -402,11 +402,31 @@ fn drain_extractor(extractor: &mut ReasoningExtractor, is_structured: bool) -> V events } +#[cfg(test)] +impl Llamacpp { + /// Build the llama.cpp wire request for `query` and serialize it to JSON + /// without sending. + /// Test-only seam for snapshotting request construction (notably compaction + /// projection) across providers. + #[expect( + clippy::unused_self, + reason = "uniform per-provider seam; only some providers read instance state" + )] + pub(crate) fn request_value( + &self, + model: &ModelDetails, + query: ChatQuery, + ) -> Result { + let (request, _) = create_request(model, query)?; + Ok(request) + } +} + /// Build the JSON request body for the llama.cpp `/v1/chat/completions` /// endpoint. /// /// Returns `(body, is_structured)`. -fn build_request(model: &ModelDetails, query: ChatQuery) -> Result<(Value, bool), Error> { +fn create_request(model: &ModelDetails, query: ChatQuery) -> Result<(Value, bool), Error> { let ChatQuery { thread, tools, diff --git a/crates/jp_llm/src/provider/ollama.rs b/crates/jp_llm/src/provider/ollama.rs index a5493c5b..d99d62d5 100644 --- a/crates/jp_llm/src/provider/ollama.rs +++ b/crates/jp_llm/src/provider/ollama.rs @@ -214,6 +214,26 @@ fn map_event( events } +#[cfg(test)] +impl Ollama { + /// Build the Ollama wire request for `query` and serialize it to JSON + /// without sending. + /// Test-only seam for snapshotting request construction (notably compaction + /// projection) across providers. + #[expect( + clippy::unused_self, + reason = "uniform per-provider seam; only some providers read instance state" + )] + pub(crate) fn request_value( + &self, + model: &ModelDetails, + query: ChatQuery, + ) -> Result { + let (request, _) = create_request(model, query)?; + Ok(serde_json::to_value(request)?) + } +} + #[expect(clippy::too_many_lines)] fn create_request(model: &ModelDetails, query: ChatQuery) -> Result<(ChatMessageRequest, bool)> { let ChatQuery { diff --git a/crates/jp_llm/src/provider/openai.rs b/crates/jp_llm/src/provider/openai.rs index fa8a1ae0..6bf97c90 100644 --- a/crates/jp_llm/src/provider/openai.rs +++ b/crates/jp_llm/src/provider/openai.rs @@ -285,6 +285,26 @@ pub(crate) struct ModelResponse { _owned_by: String, } +#[cfg(test)] +impl Openai { + /// Build the OpenAI wire request for `query` and serialize it to JSON + /// without sending. + /// Test-only seam for snapshotting request construction (notably compaction + /// projection) across providers. + #[expect( + clippy::unused_self, + reason = "uniform per-provider seam; only some providers read instance state" + )] + pub(crate) fn request_value( + &self, + model: &ModelDetails, + query: ChatQuery, + ) -> Result { + let (request, ..) = create_request(model, query)?; + Ok(serde_json::to_value(request)?) + } +} + /// Create a request for the given model and query details. /// /// Returns `(request, is_structured, reasoning_enabled)`. diff --git a/crates/jp_llm/src/provider/openrouter.rs b/crates/jp_llm/src/provider/openrouter.rs index 830015e6..c9ebdd67 100644 --- a/crates/jp_llm/src/provider/openrouter.rs +++ b/crates/jp_llm/src/provider/openrouter.rs @@ -118,7 +118,7 @@ impl Provider for Openrouter { "Starting OpenRouter chat completion stream." ); - let (request, is_structured) = build_request(query, model)?; + let (request, is_structured) = create_request(model, query)?; let mut state = AggregationState { tool_call_indices: Vec::new(), aggregating_reasoning: false, @@ -445,12 +445,32 @@ fn map_event( events } -/// Build request for Openrouter API. +#[cfg(test)] +impl Openrouter { + /// Build the OpenRouter wire request for `query` and serialize it to JSON + /// without sending. + /// Test-only seam for snapshotting request construction (notably compaction + /// projection) across providers. + #[expect( + clippy::unused_self, + reason = "uniform per-provider seam; only some providers read instance state" + )] + pub(crate) fn request_value( + &self, + model: &ModelDetails, + query: ChatQuery, + ) -> Result { + let (request, _) = create_request(model, query)?; + Ok(serde_json::to_value(request)?) + } +} + +/// Create the request for the OpenRouter API. /// /// Returns the request and whether structured output is active. -fn build_request( - query: ChatQuery, +fn create_request( model: &ModelDetails, + query: ChatQuery, ) -> Result<(request::ChatCompletion, bool)> { let ChatQuery { thread, diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/baseline.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/baseline.snap new file mode 100644 index 00000000..a8ca5b38 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/baseline.snap @@ -0,0 +1,109 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_1", + "input": { + "path": "notes.md" + }, + "name": "read_file" + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": "call_1", + "content": "- buy milk\n- call dentist", + "is_error": false + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/reasoning_strip.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/reasoning_strip.snap new file mode 100644 index 00000000..68fb992e --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/reasoning_strip.snap @@ -0,0 +1,105 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_1", + "input": { + "path": "notes.md" + }, + "name": "read_file" + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": "call_1", + "content": "- buy milk\n- call dentist", + "is_error": false + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/summary.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/summary.snap new file mode 100644 index 00000000..4ea343b4 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/summary.snap @@ -0,0 +1,45 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "[Summary of previous conversation]" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Earlier: the user asked about France's capital and had their notes read." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/summary_overlap.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/summary_overlap.snap new file mode 100644 index 00000000..de180da5 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/summary_overlap.snap @@ -0,0 +1,63 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "[Summary of previous conversation]" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Summary A: France's capital and the start of the notes lookup." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "[Summary of previous conversation]" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Summary B: the notes lookup and Germany's capital." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_omit.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_omit.snap new file mode 100644 index 00000000..bae5b0a4 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_omit.snap @@ -0,0 +1,85 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_both.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_both.snap new file mode 100644 index 00000000..c7ba663d --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_both.snap @@ -0,0 +1,107 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_1", + "input": {}, + "name": "read_file" + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": "call_1", + "content": "[compacted] read_file: success", + "is_error": false + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_request.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_request.snap new file mode 100644 index 00000000..94c430e3 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_request.snap @@ -0,0 +1,107 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_1", + "input": {}, + "name": "read_file" + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": "call_1", + "content": "- buy milk\n- call dentist", + "is_error": false + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_response.snap b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_response.snap new file mode 100644 index 00000000..4edcc4f4 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/anthropic/compaction/tool_strip_response.snap @@ -0,0 +1,109 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_1", + "input": { + "path": "notes.md" + }, + "name": "read_file" + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": "call_1", + "content": "[compacted] read_file: success", + "is_error": false + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "model": "claude-haiku-4-5", + "max_tokens": 64000, + "thinking": { + "type": "disabled" + }, + "stream": true, + "cache_control": { + "type": "ephemeral", + "ttl": "5m" + } +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/baseline.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/baseline.snap new file mode 100644 index 00000000..41a6c036 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/baseline.snap @@ -0,0 +1,58 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "- buy milk\n- call dentist" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/reasoning_strip.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/reasoning_strip.snap new file mode 100644 index 00000000..08515f1a --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/reasoning_strip.snap @@ -0,0 +1,57 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "- buy milk\n- call dentist" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/summary.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/summary.snap new file mode 100644 index 00000000..3cd1c6d1 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/summary.snap @@ -0,0 +1,23 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "role": "assistant", + "content": "Earlier: the user asked about France's capital and had their notes read." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/summary_overlap.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/summary_overlap.snap new file mode 100644 index 00000000..66b3502e --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/summary_overlap.snap @@ -0,0 +1,31 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "role": "assistant", + "content": "Summary A: France's capital and the start of the notes lookup." + }, + { + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "role": "assistant", + "content": "Summary B: the notes lookup and Germany's capital." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_omit.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_omit.snap new file mode 100644 index 00000000..9682be3d --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_omit.snap @@ -0,0 +1,40 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_both.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_both.snap new file mode 100644 index 00000000..e47e0f0f --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_both.snap @@ -0,0 +1,58 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "[compacted] read_file: success" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_request.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_request.snap new file mode 100644 index 00000000..f9f23271 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_request.snap @@ -0,0 +1,58 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "- buy milk\n- call dentist" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_response.snap b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_response.snap new file mode 100644 index 00000000..8f44611d --- /dev/null +++ b/crates/jp_llm/tests/fixtures/cerebras/compaction/tool_strip_response.snap @@ -0,0 +1,58 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-oss-120b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "[compacted] read_file: success" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "parsed" +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/baseline.snap b/crates/jp_llm/tests/fixtures/google/compaction/baseline.snap new file mode 100644 index 00000000..0ced4fa1 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/baseline.snap @@ -0,0 +1,111 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "What is the capital of France?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "thought": true, + "text": "The user wants a capital city." + }, + { + "text": "Paris." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "Read my notes file." + } + ], + "role": "user" + }, + { + "parts": [ + { + "functionCall": { + "id": "call_1", + "name": "read_file", + "args": { + "path": "notes.md" + } + }, + "thoughtSignature": "skip_thought_signature_validator" + } + ], + "role": "model" + }, + { + "parts": [ + { + "functionResponse": { + "id": "call_1", + "name": "read_file", + "response": { + "content": "- buy milk\n- call dentist" + } + } + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Your notes mention milk and the dentist." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And the capital of Germany?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Berlin." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/reasoning_strip.snap b/crates/jp_llm/tests/fixtures/google/compaction/reasoning_strip.snap new file mode 100644 index 00000000..05e0295a --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/reasoning_strip.snap @@ -0,0 +1,107 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "What is the capital of France?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Paris." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "Read my notes file." + } + ], + "role": "user" + }, + { + "parts": [ + { + "functionCall": { + "id": "call_1", + "name": "read_file", + "args": { + "path": "notes.md" + } + }, + "thoughtSignature": "skip_thought_signature_validator" + } + ], + "role": "model" + }, + { + "parts": [ + { + "functionResponse": { + "id": "call_1", + "name": "read_file", + "response": { + "content": "- buy milk\n- call dentist" + } + } + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Your notes mention milk and the dentist." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And the capital of Germany?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Berlin." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/summary.snap b/crates/jp_llm/tests/fixtures/google/compaction/summary.snap new file mode 100644 index 00000000..75753b29 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/summary.snap @@ -0,0 +1,46 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "[Summary of previous conversation]" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Earlier: the user asked about France's capital and had their notes read." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/summary_overlap.snap b/crates/jp_llm/tests/fixtures/google/compaction/summary_overlap.snap new file mode 100644 index 00000000..c30f6432 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/summary_overlap.snap @@ -0,0 +1,62 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "[Summary of previous conversation]" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Summary A: France's capital and the start of the notes lookup." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "[Summary of previous conversation]" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Summary B: the notes lookup and Germany's capital." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/tool_omit.snap b/crates/jp_llm/tests/fixtures/google/compaction/tool_omit.snap new file mode 100644 index 00000000..1dd6bc3f --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/tool_omit.snap @@ -0,0 +1,82 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "What is the capital of France?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "thought": true, + "text": "The user wants a capital city." + }, + { + "text": "Paris." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "Read my notes file." + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Your notes mention milk and the dentist." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And the capital of Germany?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Berlin." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_both.snap b/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_both.snap new file mode 100644 index 00000000..1a6678cb --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_both.snap @@ -0,0 +1,109 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "What is the capital of France?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "thought": true, + "text": "The user wants a capital city." + }, + { + "text": "Paris." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "Read my notes file." + } + ], + "role": "user" + }, + { + "parts": [ + { + "functionCall": { + "id": "call_1", + "name": "read_file", + "args": {} + }, + "thoughtSignature": "skip_thought_signature_validator" + } + ], + "role": "model" + }, + { + "parts": [ + { + "functionResponse": { + "id": "call_1", + "name": "read_file", + "response": { + "content": "[compacted] read_file: success" + } + } + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Your notes mention milk and the dentist." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And the capital of Germany?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Berlin." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_request.snap b/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_request.snap new file mode 100644 index 00000000..a18d31e5 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_request.snap @@ -0,0 +1,109 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "What is the capital of France?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "thought": true, + "text": "The user wants a capital city." + }, + { + "text": "Paris." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "Read my notes file." + } + ], + "role": "user" + }, + { + "parts": [ + { + "functionCall": { + "id": "call_1", + "name": "read_file", + "args": {} + }, + "thoughtSignature": "skip_thought_signature_validator" + } + ], + "role": "model" + }, + { + "parts": [ + { + "functionResponse": { + "id": "call_1", + "name": "read_file", + "response": { + "content": "- buy milk\n- call dentist" + } + } + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Your notes mention milk and the dentist." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And the capital of Germany?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Berlin." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_response.snap b/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_response.snap new file mode 100644 index 00000000..eabf78f4 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/google/compaction/tool_strip_response.snap @@ -0,0 +1,111 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "contents": [ + { + "parts": [ + { + "text": "What is the capital of France?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "thought": true, + "text": "The user wants a capital city." + }, + { + "text": "Paris." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "Read my notes file." + } + ], + "role": "user" + }, + { + "parts": [ + { + "functionCall": { + "id": "call_1", + "name": "read_file", + "args": { + "path": "notes.md" + } + }, + "thoughtSignature": "skip_thought_signature_validator" + } + ], + "role": "model" + }, + { + "parts": [ + { + "functionResponse": { + "id": "call_1", + "name": "read_file", + "response": { + "content": "[compacted] read_file: success" + } + } + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Your notes mention milk and the dentist." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And the capital of Germany?" + } + ], + "role": "user" + }, + { + "parts": [ + { + "text": "Berlin." + } + ], + "role": "model" + }, + { + "parts": [ + { + "text": "And of Italy?" + } + ], + "role": "user" + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "NONE", + "allowedFunctionNames": [] + } + }, + "generationConfig": { + "maxOutputTokens": 64000, + "thinkingConfig": { + "includeThoughts": false, + "thinkingBudget": 512, + "thinkingLevel": null + } + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/baseline.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/baseline.snap new file mode 100644 index 00000000..a1402757 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/baseline.snap @@ -0,0 +1,61 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning_content": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "- buy milk\n- call dentist" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/reasoning_strip.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/reasoning_strip.snap new file mode 100644 index 00000000..4cc4d283 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/reasoning_strip.snap @@ -0,0 +1,60 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "- buy milk\n- call dentist" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/summary.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/summary.snap new file mode 100644 index 00000000..a5bb4236 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/summary.snap @@ -0,0 +1,26 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "role": "assistant", + "content": "Earlier: the user asked about France's capital and had their notes read." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/summary_overlap.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/summary_overlap.snap new file mode 100644 index 00000000..fd87b1ef --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/summary_overlap.snap @@ -0,0 +1,34 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "role": "assistant", + "content": "Summary A: France's capital and the start of the notes lookup." + }, + { + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "role": "assistant", + "content": "Summary B: the notes lookup and Germany's capital." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_omit.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_omit.snap new file mode 100644 index 00000000..1be3a211 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_omit.snap @@ -0,0 +1,43 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning_content": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_both.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_both.snap new file mode 100644 index 00000000..58af5b52 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_both.snap @@ -0,0 +1,61 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning_content": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "[compacted] read_file: success" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_request.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_request.snap new file mode 100644 index 00000000..471ad751 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_request.snap @@ -0,0 +1,61 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning_content": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "- buy milk\n- call dentist" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_response.snap b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_response.snap new file mode 100644 index 00000000..cd1af4f6 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/llamacpp/compaction/tool_strip_response.snap @@ -0,0 +1,61 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + }, + { + "role": "assistant", + "reasoning_content": "The user wants a capital city.", + "content": "Paris." + }, + { + "role": "user", + "content": "Read my notes file." + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_1", + "content": "[compacted] read_file: success" + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "role": "user", + "content": "And the capital of Germany?" + }, + { + "role": "assistant", + "content": "Berlin." + }, + { + "role": "user", + "content": "And of Italy?" + } + ], + "stream": true, + "reasoning_format": "none", + "chat_template_kwargs": { + "enable_thinking": false + } +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/baseline.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/baseline.snap new file mode 100644 index 00000000..42b4c7ef --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/baseline.snap @@ -0,0 +1,81 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [], + "thinking": "The user wants a capital city." + }, + { + "role": "assistant", + "content": "Paris.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "Read my notes file.", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "function": { + "name": "read_file", + "arguments": { + "path": "notes.md" + } + } + } + ], + "thinking": null + }, + { + "role": "tool", + "content": "- buy milk\n- call dentist", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And the capital of Germany?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Berlin.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/reasoning_strip.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/reasoning_strip.snap new file mode 100644 index 00000000..ff821945 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/reasoning_strip.snap @@ -0,0 +1,75 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Paris.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "Read my notes file.", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "function": { + "name": "read_file", + "arguments": { + "path": "notes.md" + } + } + } + ], + "thinking": null + }, + { + "role": "tool", + "content": "- buy milk\n- call dentist", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And the capital of Germany?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Berlin.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/summary.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/summary.snap new file mode 100644 index 00000000..b3ec724b --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/summary.snap @@ -0,0 +1,30 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "[Summary of previous conversation]", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Earlier: the user asked about France's capital and had their notes read.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/summary_overlap.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/summary_overlap.snap new file mode 100644 index 00000000..6b9b7adc --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/summary_overlap.snap @@ -0,0 +1,42 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "[Summary of previous conversation]", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Summary A: France's capital and the start of the notes lookup.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "[Summary of previous conversation]", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Summary B: the notes lookup and Germany's capital.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/tool_omit.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_omit.snap new file mode 100644 index 00000000..4fecc028 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_omit.snap @@ -0,0 +1,60 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [], + "thinking": "The user wants a capital city." + }, + { + "role": "assistant", + "content": "Paris.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "Read my notes file.", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And the capital of Germany?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Berlin.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_both.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_both.snap new file mode 100644 index 00000000..785ca564 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_both.snap @@ -0,0 +1,79 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [], + "thinking": "The user wants a capital city." + }, + { + "role": "assistant", + "content": "Paris.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "Read my notes file.", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "function": { + "name": "read_file", + "arguments": {} + } + } + ], + "thinking": null + }, + { + "role": "tool", + "content": "[compacted] read_file: success", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And the capital of Germany?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Berlin.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_request.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_request.snap new file mode 100644 index 00000000..9a9641de --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_request.snap @@ -0,0 +1,79 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [], + "thinking": "The user wants a capital city." + }, + { + "role": "assistant", + "content": "Paris.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "Read my notes file.", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "function": { + "name": "read_file", + "arguments": {} + } + } + ], + "thinking": null + }, + { + "role": "tool", + "content": "- buy milk\n- call dentist", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And the capital of Germany?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Berlin.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_response.snap b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_response.snap new file mode 100644 index 00000000..bf643f67 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/ollama/compaction/tool_strip_response.snap @@ -0,0 +1,81 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "qwen3.5:9b", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [], + "thinking": "The user wants a capital city." + }, + { + "role": "assistant", + "content": "Paris.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "Read my notes file.", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "function": { + "name": "read_file", + "arguments": { + "path": "notes.md" + } + } + } + ], + "thinking": null + }, + { + "role": "tool", + "content": "[compacted] read_file: success", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Your notes mention milk and the dentist.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And the capital of Germany?", + "tool_calls": [], + "thinking": null + }, + { + "role": "assistant", + "content": "Berlin.", + "tool_calls": [], + "thinking": null + }, + { + "role": "user", + "content": "And of Italy?", + "tool_calls": [], + "thinking": null + } + ], + "options": {}, + "stream": false, + "think": false +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/baseline.snap b/crates/jp_llm/tests/fixtures/openai/compaction/baseline.snap new file mode 100644 index 00000000..98eec5e9 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/baseline.snap @@ -0,0 +1,89 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "What is the capital of France?" + }, + { + "type": "message", + "role": "assistant", + "content": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "message", + "role": "assistant", + "content": "Paris." + }, + { + "type": "message", + "role": "user", + "content": "Read my notes file." + }, + { + "type": "function_call", + "arguments": "{\"path\":\"notes.md\"}", + "call_id": "call_1", + "id": null, + "name": "read_file", + "status": null + }, + { + "type": "function_call_output", + "id": null, + "status": null, + "call_id": "call_1", + "output": "- buy milk\n- call dentist" + }, + { + "type": "message", + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "type": "message", + "role": "user", + "content": "And the capital of Germany?" + }, + { + "type": "message", + "role": "assistant", + "content": "Berlin." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/reasoning_strip.snap b/crates/jp_llm/tests/fixtures/openai/compaction/reasoning_strip.snap new file mode 100644 index 00000000..b1f0d617 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/reasoning_strip.snap @@ -0,0 +1,84 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "What is the capital of France?" + }, + { + "type": "message", + "role": "assistant", + "content": "Paris." + }, + { + "type": "message", + "role": "user", + "content": "Read my notes file." + }, + { + "type": "function_call", + "arguments": "{\"path\":\"notes.md\"}", + "call_id": "call_1", + "id": null, + "name": "read_file", + "status": null + }, + { + "type": "function_call_output", + "id": null, + "status": null, + "call_id": "call_1", + "output": "- buy milk\n- call dentist" + }, + { + "type": "message", + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "type": "message", + "role": "user", + "content": "And the capital of Germany?" + }, + { + "type": "message", + "role": "assistant", + "content": "Berlin." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/summary.snap b/crates/jp_llm/tests/fixtures/openai/compaction/summary.snap new file mode 100644 index 00000000..fa1c6b3d --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/summary.snap @@ -0,0 +1,49 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "type": "message", + "role": "assistant", + "content": "Earlier: the user asked about France's capital and had their notes read." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/summary_overlap.snap b/crates/jp_llm/tests/fixtures/openai/compaction/summary_overlap.snap new file mode 100644 index 00000000..9fc77a72 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/summary_overlap.snap @@ -0,0 +1,59 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "type": "message", + "role": "assistant", + "content": "Summary A: France's capital and the start of the notes lookup." + }, + { + "type": "message", + "role": "user", + "content": "[Summary of previous conversation]" + }, + { + "type": "message", + "role": "assistant", + "content": "Summary B: the notes lookup and Germany's capital." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/tool_omit.snap b/crates/jp_llm/tests/fixtures/openai/compaction/tool_omit.snap new file mode 100644 index 00000000..d52e1f8a --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/tool_omit.snap @@ -0,0 +1,74 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "What is the capital of France?" + }, + { + "type": "message", + "role": "assistant", + "content": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "message", + "role": "assistant", + "content": "Paris." + }, + { + "type": "message", + "role": "user", + "content": "Read my notes file." + }, + { + "type": "message", + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "type": "message", + "role": "user", + "content": "And the capital of Germany?" + }, + { + "type": "message", + "role": "assistant", + "content": "Berlin." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_both.snap b/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_both.snap new file mode 100644 index 00000000..5b0cada0 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_both.snap @@ -0,0 +1,89 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "What is the capital of France?" + }, + { + "type": "message", + "role": "assistant", + "content": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "message", + "role": "assistant", + "content": "Paris." + }, + { + "type": "message", + "role": "user", + "content": "Read my notes file." + }, + { + "type": "function_call", + "arguments": "{}", + "call_id": "call_1", + "id": null, + "name": "read_file", + "status": null + }, + { + "type": "function_call_output", + "id": null, + "status": null, + "call_id": "call_1", + "output": "[compacted] read_file: success" + }, + { + "type": "message", + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "type": "message", + "role": "user", + "content": "And the capital of Germany?" + }, + { + "type": "message", + "role": "assistant", + "content": "Berlin." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_request.snap b/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_request.snap new file mode 100644 index 00000000..a0f08da4 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_request.snap @@ -0,0 +1,89 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "What is the capital of France?" + }, + { + "type": "message", + "role": "assistant", + "content": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "message", + "role": "assistant", + "content": "Paris." + }, + { + "type": "message", + "role": "user", + "content": "Read my notes file." + }, + { + "type": "function_call", + "arguments": "{}", + "call_id": "call_1", + "id": null, + "name": "read_file", + "status": null + }, + { + "type": "function_call_output", + "id": null, + "status": null, + "call_id": "call_1", + "output": "- buy milk\n- call dentist" + }, + { + "type": "message", + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "type": "message", + "role": "user", + "content": "And the capital of Germany?" + }, + { + "type": "message", + "role": "assistant", + "content": "Berlin." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_response.snap b/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_response.snap new file mode 100644 index 00000000..f351a108 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openai/compaction/tool_strip_response.snap @@ -0,0 +1,89 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "gpt-5-mini", + "input": [ + { + "type": "message", + "role": "system", + "content": [] + }, + { + "type": "message", + "role": "user", + "content": "What is the capital of France?" + }, + { + "type": "message", + "role": "assistant", + "content": "\nThe user wants a capital city.\n\n\n" + }, + { + "type": "message", + "role": "assistant", + "content": "Paris." + }, + { + "type": "message", + "role": "user", + "content": "Read my notes file." + }, + { + "type": "function_call", + "arguments": "{\"path\":\"notes.md\"}", + "call_id": "call_1", + "id": null, + "name": "read_file", + "status": null + }, + { + "type": "function_call_output", + "id": null, + "status": null, + "call_id": "call_1", + "output": "[compacted] read_file: success" + }, + { + "type": "message", + "role": "assistant", + "content": "Your notes mention milk and the dentist." + }, + { + "type": "message", + "role": "user", + "content": "And the capital of Germany?" + }, + { + "type": "message", + "role": "assistant", + "content": "Berlin." + }, + { + "type": "message", + "role": "user", + "content": "And of Italy?" + } + ], + "include": null, + "instructions": null, + "max_output_tokens": null, + "metadata": null, + "parallel_tool_calls": null, + "previous_response_id": null, + "reasoning": { + "effort": "minimal", + "summary": "auto" + }, + "service_tier": null, + "store": false, + "stream": null, + "temperature": null, + "text": null, + "tool_choice": "auto", + "tools": [], + "top_p": null, + "truncation": "auto", + "user": null +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/baseline.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/baseline.snap new file mode 100644 index 00000000..629331ee --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/baseline.snap @@ -0,0 +1,97 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "index": 0, + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "content": "- buy milk\n- call dentist", + "tool_call_id": "call_1" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/reasoning_strip.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/reasoning_strip.snap new file mode 100644 index 00000000..7820f5f9 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/reasoning_strip.snap @@ -0,0 +1,94 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "index": 0, + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "content": "- buy milk\n- call dentist", + "tool_call_id": "call_1" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/summary.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/summary.snap new file mode 100644 index 00000000..d15fafe5 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/summary.snap @@ -0,0 +1,40 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "[Summary of previous conversation]" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Earlier: the user asked about France's capital and had their notes read." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/summary_overlap.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/summary_overlap.snap new file mode 100644 index 00000000..3df23e4d --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/summary_overlap.snap @@ -0,0 +1,58 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "[Summary of previous conversation]" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Summary A: France's capital and the start of the notes lookup." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "[Summary of previous conversation]" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Summary B: the notes lookup and Germany's capital." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_omit.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_omit.snap new file mode 100644 index 00000000..5b90ba49 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_omit.snap @@ -0,0 +1,79 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_both.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_both.snap new file mode 100644 index 00000000..463ece79 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_both.snap @@ -0,0 +1,97 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "index": 0, + "function": { + "name": "read_file", + "arguments": null + } + } + ] + }, + { + "role": "tool", + "content": "[compacted] read_file: success", + "tool_call_id": "call_1" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_request.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_request.snap new file mode 100644 index 00000000..1dd99d38 --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_request.snap @@ -0,0 +1,97 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "index": 0, + "function": { + "name": "read_file", + "arguments": null + } + } + ] + }, + { + "role": "tool", + "content": "- buy milk\n- call dentist", + "tool_call_id": "call_1" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +} diff --git a/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_response.snap b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_response.snap new file mode 100644 index 00000000..b7b4f90c --- /dev/null +++ b/crates/jp_llm/tests/fixtures/openrouter/compaction/tool_strip_response.snap @@ -0,0 +1,97 @@ +--- +source: crates/jp_llm/src/provider/compaction_request_tests.rs +expression: request +--- +{ + "model": "openai/gpt-5-mini", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is the capital of France?" + } + ] + }, + { + "role": "assistant" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Paris." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Read my notes file." + } + ] + }, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "index": 0, + "function": { + "name": "read_file", + "arguments": "{\"path\":\"notes.md\"}" + } + } + ] + }, + { + "role": "tool", + "content": "[compacted] read_file: success", + "tool_call_id": "call_1" + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Your notes mention milk and the dentist." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And the capital of Germany?" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Berlin." + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "And of Italy?" + } + ] + } + ], + "reasoning": { + "exclude": true, + "effort": "minimal" + } +}