app-server/src/dynamic_tools.rs
75 lines
use codex_app_server_protocol::DynamicToolCallOutputContentItem;use codex_app_server_protocol::DynamicToolCallResponse;use codex_core::CodexThread;use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem as CoreDynamicToolCallOutputContentItem;use codex_protocol::dynamic_tools::DynamicToolResponse as CoreDynamicToolResponse;use codex_protocol::protocol::Op;use std::sync::Arc;use tokio::sync::oneshot;use tracing::error;use crate::outgoing_message::ClientRequestResult;use crate::server_request_error::is_turn_transition_server_request_error;pub(crate) async fn on_call_response( call_id: String, receiver: oneshot::Receiver<ClientRequestResult>, conversation: Arc<CodexThread>,) { let response = receiver.await; let (response, _error) = match response { Ok(Ok(value)) => decode_response(value), Ok(Err(err)) if is_turn_transition_server_request_error(&err) => return, Ok(Err(err)) => { error!("request failed with client error: {err:?}"); fallback_response("dynamic tool request failed") } Err(err) => { error!("request failed: {err:?}"); fallback_response("dynamic tool request failed") } }; let DynamicToolCallResponse { content_items, success, } = response.clone(); let core_response = CoreDynamicToolResponse { content_items: content_items .into_iter() .map(CoreDynamicToolCallOutputContentItem::from) .collect(), success, }; if let Err(err) = conversation .submit(Op::DynamicToolResponse { id: call_id.clone(), response: core_response, }) .await { error!("failed to submit DynamicToolResponse: {err}"); }}fn decode_response(value: serde_json::Value) -> (DynamicToolCallResponse, Option<String>) { match serde_json::from_value::<DynamicToolCallResponse>(value) { Ok(response) => (response, None), Err(err) => { error!("failed to deserialize DynamicToolCallResponse: {err}"); fallback_response("dynamic tool response was invalid") } }}fn fallback_response(message: &str) -> (DynamicToolCallResponse, Option<String>) { ( DynamicToolCallResponse { content_items: vec![DynamicToolCallOutputContentItem::InputText { text: message.to_string(), }], success: false, }, Some(message.to_string()), )}