Skip to content

Commit 8ff661c

Browse files
committed
Wire runtime thread resume to the active sessions dir
Pass the runtime API sessions directory through RuntimeThreadManagerConfig so thread resume and engine reload use the same session source as the HTTP surface. This also adds an integration-style regression test that proves thinking blocks survive resume when sessions live in a custom directory.
1 parent 74e7e24 commit 8ff661c

2 files changed

Lines changed: 88 additions & 2 deletions

File tree

crates/tui/src/runtime_api.rs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,10 +470,18 @@ pub async fn run_http_server(
470470
config.default_text_model.clone(),
471471
Some(options.workers),
472472
);
473+
let sessions_dir = default_sessions_dir().unwrap_or_else(|_| {
474+
task_cfg
475+
.data_dir
476+
.parent()
477+
.unwrap_or(&task_cfg.data_dir)
478+
.join("sessions")
479+
});
473480
let runtime_threads = Arc::new(RuntimeThreadManager::open(
474481
config.clone(),
475482
workspace.clone(),
476-
RuntimeThreadManagerConfig::from_task_data_dir(task_cfg.data_dir.clone()),
483+
RuntimeThreadManagerConfig::from_task_data_dir(task_cfg.data_dir.clone())
484+
.with_sessions_dir(sessions_dir.clone()),
477485
)?);
478486
let task_manager =
479487
TaskManager::start_with_runtime_manager(task_cfg, config.clone(), runtime_threads.clone())
@@ -3718,7 +3726,8 @@ mod tests {
37183726
let runtime_threads: SharedRuntimeThreadManager = Arc::new(RuntimeThreadManager::open(
37193727
Config::default(),
37203728
workspace.clone(),
3721-
RuntimeThreadManagerConfig::from_task_data_dir(root.join("runtime")),
3729+
RuntimeThreadManagerConfig::from_task_data_dir(root.join("runtime"))
3730+
.with_sessions_dir(sessions_dir.clone()),
37223731
)?);
37233732
runtime_threads.attach_task_manager(manager.clone());
37243733
let automations = Arc::new(Mutex::new(AutomationManager::open(
@@ -5410,6 +5419,77 @@ mod tests {
54105419
Ok(())
54115420
}
54125421

5422+
#[tokio::test]
5423+
async fn session_resume_thread_engine_load_preserves_thinking_from_custom_sessions_dir()
5424+
-> Result<()> {
5425+
let root = std::env::temp_dir().join(format!(
5426+
"deepseek-session-resume-thinking-{}",
5427+
Uuid::new_v4()
5428+
));
5429+
let sessions_dir = root.join("sessions");
5430+
fs::create_dir_all(&sessions_dir)?;
5431+
let session = json!({
5432+
"schema_version": 1,
5433+
"metadata": {
5434+
"id": "sess_test_resume_thinking",
5435+
"title": "Test resume session with thinking",
5436+
"created_at": "2025-01-01T00:00:00Z",
5437+
"updated_at": "2025-01-01T00:10:00Z",
5438+
"message_count": 2,
5439+
"total_tokens": 100,
5440+
"model": "deepseek-v4-pro",
5441+
"workspace": "/tmp/test",
5442+
"mode": "agent"
5443+
},
5444+
"messages": [
5445+
{
5446+
"role": "user",
5447+
"content": [{ "type": "text", "text": "Hello, world!" }]
5448+
},
5449+
{
5450+
"role": "assistant",
5451+
"content": [{ "type": "thinking", "thinking": "internal chain" }]
5452+
}
5453+
],
5454+
"system_prompt": null
5455+
});
5456+
fs::write(
5457+
sessions_dir.join("sess_test_resume_thinking.json"),
5458+
serde_json::to_string_pretty(&session)?,
5459+
)?;
5460+
5461+
let Some((addr, runtime_threads, handle)) =
5462+
spawn_test_server_with_root(root.clone(), sessions_dir.clone()).await?
5463+
else {
5464+
return Ok(());
5465+
};
5466+
let client = crate::tls::reqwest_client();
5467+
5468+
let resp = client
5469+
.post(format!(
5470+
"http://{addr}/v1/sessions/sess_test_resume_thinking/resume-thread"
5471+
))
5472+
.json(&json!({ "model": "deepseek-v4-pro" }))
5473+
.send()
5474+
.await?;
5475+
assert_eq!(resp.status(), StatusCode::CREATED);
5476+
let resumed: serde_json::Value = resp.json().await?;
5477+
let thread_id = resumed["thread_id"]
5478+
.as_str()
5479+
.context("missing resumed thread id")?;
5480+
5481+
let engine = runtime_threads.get_engine(thread_id).await?;
5482+
let snapshot = engine.get_session_snapshot().await?;
5483+
assert_eq!(snapshot.messages.len(), 2);
5484+
assert!(matches!(
5485+
&snapshot.messages[1].content[0],
5486+
ContentBlock::Thinking { thinking, .. } if thinking == "internal chain"
5487+
));
5488+
5489+
handle.abort();
5490+
Ok(())
5491+
}
5492+
54135493
#[tokio::test]
54145494
async fn session_create_from_completed_thread_saves_messages() -> Result<()> {
54155495
let root = std::env::temp_dir().join(format!("deepseek-thread-session-{}", Uuid::new_v4()));

crates/tui/src/runtime_threads.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,12 @@ impl RuntimeThreadManagerConfig {
607607
max_active_threads: MAX_ACTIVE_THREADS_DEFAULT,
608608
}
609609
}
610+
611+
#[must_use]
612+
pub fn with_sessions_dir(mut self, sessions_dir: PathBuf) -> Self {
613+
self.sessions_dir = Some(sessions_dir);
614+
self
615+
}
610616
}
611617

612618
/// Visibility filter for `list_threads`. Default is `ActiveOnly`. The runtime

0 commit comments

Comments
 (0)