mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
98 lines
3.4 KiB
Rust
98 lines
3.4 KiB
Rust
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
use std::sync::Arc;
|
|
|
|
use fabro_agent::{AgentProfile, LocalSandbox, OpenAiProfile, Session, SessionOptions};
|
|
use fabro_llm::client::Client;
|
|
use fabro_llm::provider::ProviderAdapter;
|
|
use fabro_llm::providers::OpenAiAdapter;
|
|
use fabro_model::ProviderId;
|
|
use fabro_test::{TwinScenario, TwinScenarios, TwinToolCall, twin_openai};
|
|
use tokio::fs::read_to_string;
|
|
|
|
const MODEL: &str = "gpt-5.4-mini";
|
|
|
|
#[expect(
|
|
clippy::disallowed_methods,
|
|
reason = "e2e_openai! expands live-mode environment lookups even for twin-only tests"
|
|
)]
|
|
#[fabro_macros::e2e_test(twin)]
|
|
async fn openai_twin_compaction_preserves_tool_call_pairs() {
|
|
let tmp = tempfile::tempdir().expect("failed to create tempdir");
|
|
let (base_url, api_key) = fabro_test::e2e_openai!();
|
|
|
|
load_compaction_scenarios(&api_key).await;
|
|
|
|
let mut session = make_openai_session(tmp.path(), base_url, api_key);
|
|
session.initialize().await.unwrap();
|
|
|
|
let result = session
|
|
.process_input(
|
|
"Trigger the compaction regression by writing four small files, then say done.",
|
|
)
|
|
.await;
|
|
|
|
assert!(
|
|
result.is_ok(),
|
|
"session should complete without sending an orphaned function_call_output: {result:?}"
|
|
);
|
|
assert_eq!(
|
|
read_to_string(tmp.path().join("four.txt"))
|
|
.await
|
|
.expect("four.txt should be written"),
|
|
"four"
|
|
);
|
|
}
|
|
|
|
fn make_openai_session(cwd: &Path, base_url: String, api_key: String) -> Session {
|
|
let adapter: Arc<dyn ProviderAdapter> =
|
|
Arc::new(OpenAiAdapter::new(api_key).with_base_url(base_url));
|
|
let mut providers = HashMap::new();
|
|
providers.insert(ProviderId::OPENAI.to_string(), adapter);
|
|
let client = Client::new(providers, Some(ProviderId::OPENAI.to_string()), Vec::new());
|
|
let profile: Arc<dyn AgentProfile> = Arc::new(OpenAiProfile::new(MODEL));
|
|
let sandbox = Arc::new(LocalSandbox::new(cwd.to_path_buf()));
|
|
let options = SessionOptions {
|
|
enable_context_compaction: true,
|
|
compaction_threshold_percent: 80,
|
|
compaction_preserve_turns: 6,
|
|
..SessionOptions::default()
|
|
};
|
|
|
|
Session::new(client, profile, sandbox, options, None)
|
|
}
|
|
|
|
async fn load_compaction_scenarios(namespace: &str) {
|
|
TwinScenarios::new(namespace.to_string())
|
|
.scenario(
|
|
TwinScenario::responses(MODEL)
|
|
.stream(true)
|
|
.input_contains("Trigger the compaction regression")
|
|
.tool_call(TwinToolCall::write_file("one.txt", "one")),
|
|
)
|
|
.scenario(
|
|
TwinScenario::responses(MODEL)
|
|
.stream(true)
|
|
.tool_call(TwinToolCall::write_file("two.txt", "two")),
|
|
)
|
|
.scenario(
|
|
TwinScenario::responses(MODEL)
|
|
.stream(true)
|
|
.tool_call(TwinToolCall::write_file("three.txt", "three")),
|
|
)
|
|
.scenario(
|
|
TwinScenario::responses(MODEL)
|
|
.stream(true)
|
|
.tool_call(TwinToolCall::write_file("four.txt", "four"))
|
|
.usage(180_000, 5),
|
|
)
|
|
.scenario(
|
|
TwinScenario::responses(MODEL)
|
|
.stream(false)
|
|
.input_contains("Here is the conversation to summarize")
|
|
.text("short summary"),
|
|
)
|
|
.scenario(TwinScenario::responses(MODEL).stream(true).text("Done."))
|
|
.load(twin_openai().await)
|
|
.await;
|
|
}
|