codex_runtime/runtime/client/
session.rs1use std::sync::atomic::{AtomicBool, Ordering};
2use std::sync::Arc;
3
4use tokio::sync::Mutex;
5
6use crate::runtime::api::{PromptRunError, PromptRunParams, PromptRunResult};
7use crate::runtime::core::Runtime;
8use crate::runtime::errors::RpcError;
9use crate::runtime::hooks::merge_hook_configs;
10
11use super::profile::{profile_to_prompt_params_with_hooks, session_prompt_params};
12use super::{RunProfile, SessionConfig};
13
14#[derive(Clone)]
15pub struct Session {
16 runtime: Runtime,
17 pub thread_id: String,
18 pub config: SessionConfig,
19 closed: Arc<AtomicBool>,
20 close_result: Arc<Mutex<Option<Result<(), RpcError>>>>,
21}
22
23impl Session {
24 pub(super) fn new(runtime: Runtime, thread_id: String, config: SessionConfig) -> Self {
25 Self {
26 runtime,
27 thread_id,
28 config,
29 closed: Arc::new(AtomicBool::new(false)),
30 close_result: Arc::new(Mutex::new(None)),
31 }
32 }
33
34 pub fn is_closed(&self) -> bool {
37 self.closed.load(Ordering::Acquire)
38 }
39
40 pub async fn ask(&self, prompt: impl Into<String>) -> Result<PromptRunResult, PromptRunError> {
44 ensure_session_open_for_prompt(self.is_closed())?;
45 self.runtime
46 .run_prompt_on_loaded_thread_with_hooks(
47 &self.thread_id,
48 session_prompt_params(&self.config, prompt),
49 Some(&self.config.hooks),
50 )
51 .await
52 }
53
54 pub async fn ask_with(
58 &self,
59 params: PromptRunParams,
60 ) -> Result<PromptRunResult, PromptRunError> {
61 ensure_session_open_for_prompt(self.is_closed())?;
62 self.runtime
63 .run_prompt_on_loaded_thread_with_hooks(
64 &self.thread_id,
65 params,
66 Some(&self.config.hooks),
67 )
68 .await
69 }
70
71 pub async fn ask_with_profile(
75 &self,
76 prompt: impl Into<String>,
77 profile: RunProfile,
78 ) -> Result<PromptRunResult, PromptRunError> {
79 ensure_session_open_for_prompt(self.is_closed())?;
80 let (params, profile_hooks) =
81 profile_to_prompt_params_with_hooks(self.config.cwd.clone(), prompt, profile);
82 let merged_hooks = merge_hook_configs(&self.config.hooks, &profile_hooks);
83 self.runtime
84 .run_prompt_on_loaded_thread_with_hooks(&self.thread_id, params, Some(&merged_hooks))
85 .await
86 }
87
88 pub fn profile(&self) -> RunProfile {
91 self.config.profile()
92 }
93
94 pub async fn interrupt_turn(&self, turn_id: &str) -> Result<(), RpcError> {
98 ensure_session_open_for_rpc(self.is_closed())?;
99 self.runtime.turn_interrupt(&self.thread_id, turn_id).await
100 }
101
102 pub async fn close(&self) -> Result<(), RpcError> {
106 let mut guard = self.close_result.lock().await;
107 if let Some(result) = guard.as_ref() {
108 return result.clone();
109 }
110
111 self.closed.store(true, Ordering::Release);
112 let result = self.runtime.thread_archive(&self.thread_id).await;
113 *guard = Some(result.clone());
114 result
115 }
116}
117
118pub(super) fn ensure_session_open_for_prompt(closed: bool) -> Result<(), PromptRunError> {
119 if closed {
120 return Err(PromptRunError::Rpc(RpcError::InvalidRequest(
121 "session is closed".to_owned(),
122 )));
123 }
124 Ok(())
125}
126
127pub(super) fn ensure_session_open_for_rpc(closed: bool) -> Result<(), RpcError> {
128 if closed {
129 return Err(RpcError::InvalidRequest("session is closed".to_owned()));
130 }
131 Ok(())
132}