Download codex-rs/app-server-test-client/src/request_user_input.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 4.85 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server-test-client/src/request_user_input.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/app-server-test-client/src/request_user_input.rs
-
curl -L -o request_user_input.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server-test-client/src/request_user_input.rs
4.85 kB
| use std::collections::HashMap; | |
| use std::io; | |
| use std::io::BufRead; | |
| use std::io::IsTerminal; | |
| use std::io::Write; | |
| use anyhow::Context; | |
| use anyhow::Result; | |
| use anyhow::bail; | |
| use codex_app_server_protocol::ToolRequestUserInputAnswer; | |
| use codex_app_server_protocol::ToolRequestUserInputParams; | |
| use codex_app_server_protocol::ToolRequestUserInputResponse; | |
| pub(super) fn prompt_for_answers( | |
| params: &ToolRequestUserInputParams, | |
| ) -> Result<ToolRequestUserInputResponse> { | |
| let stdin = io::stdin(); | |
| if !stdin.is_terminal() { | |
| bail!("request_user_input requires an interactive stdin terminal"); | |
| } | |
| let stdout = io::stdout(); | |
| prompt_for_answers_with(&mut stdin.lock(), &mut stdout.lock(), params) | |
| } | |
| fn prompt_for_answers_with( | |
| input: &mut impl BufRead, | |
| output: &mut impl Write, | |
| params: &ToolRequestUserInputParams, | |
| ) -> Result<ToolRequestUserInputResponse> { | |
| writeln!( | |
| output, | |
| "\n[request_user_input for thread {}, turn {}]", | |
| params.thread_id, params.turn_id | |
| )?; | |
| if !params.is_blocking { | |
| writeln!(output, "This request is non-blocking.")?; | |
| } | |
| let mut answers = HashMap::new(); | |
| for question in ¶ms.questions { | |
| writeln!(output, "\n{}: {}", question.header, question.question)?; | |
| let options = question | |
| .options | |
| .as_deref() | |
| .filter(|options| !options.is_empty()); | |
| let answer_values = if let Some(options) = options { | |
| for (index, option) in options.iter().enumerate() { | |
| writeln!( | |
| output, | |
| " {}. {} - {}", | |
| index + 1, | |
| option.label, | |
| option.description | |
| )?; | |
| } | |
| if question.is_other { | |
| writeln!(output, " o. Other (free-form)")?; | |
| } | |
| loop { | |
| if question.is_other { | |
| write!(output, "Choose 1-{} or o: ", options.len())?; | |
| } else { | |
| write!(output, "Choose 1-{}: ", options.len())?; | |
| } | |
| output.flush()?; | |
| let mut line = String::new(); | |
| if input | |
| .read_line(&mut line) | |
| .context("failed to read request_user_input selection")? | |
| == 0 | |
| { | |
| bail!("stdin closed while waiting for request_user_input selection"); | |
| } | |
| let selection = line.trim(); | |
| if let Ok(index) = selection.parse::<usize>() | |
| && let Some(option) = index.checked_sub(1).and_then(|index| options.get(index)) | |
| { | |
| break vec![option.label.clone()]; | |
| } | |
| if let Some(option) = options | |
| .iter() | |
| .find(|option| option.label.eq_ignore_ascii_case(selection)) | |
| { | |
| break vec![option.label.clone()]; | |
| } | |
| if question.is_other && selection.eq_ignore_ascii_case("o") { | |
| write!(output, "Other: ")?; | |
| output.flush()?; | |
| line.clear(); | |
| if input | |
| .read_line(&mut line) | |
| .context("failed to read request_user_input free-form answer")? | |
| == 0 | |
| { | |
| bail!("stdin closed while waiting for request_user_input free-form answer"); | |
| } | |
| let answer = line.trim(); | |
| if !answer.is_empty() { | |
| break vec![format!("user_note: {answer}")]; | |
| } | |
| } | |
| writeln!(output, "Invalid selection; try again.")?; | |
| } | |
| } else { | |
| loop { | |
| write!(output, "Answer: ")?; | |
| output.flush()?; | |
| let mut line = String::new(); | |
| if input | |
| .read_line(&mut line) | |
| .context("failed to read request_user_input answer")? | |
| == 0 | |
| { | |
| bail!("stdin closed while waiting for request_user_input answer"); | |
| } | |
| let answer = line.trim(); | |
| if !answer.is_empty() { | |
| break vec![format!("user_note: {answer}")]; | |
| } | |
| writeln!(output, "Answer cannot be empty; try again.")?; | |
| } | |
| }; | |
| answers.insert( | |
| question.id.clone(), | |
| ToolRequestUserInputAnswer { | |
| answers: answer_values, | |
| }, | |
| ); | |
| } | |
| Ok(ToolRequestUserInputResponse { answers }) | |
| } | |
| mod tests; | |