Add feedback upload request handling (#5682)
This commit is contained in:
@@ -52,6 +52,8 @@ use codex_app_server_protocol::ServerRequestPayload;
|
||||
use codex_app_server_protocol::SessionConfiguredNotification;
|
||||
use codex_app_server_protocol::SetDefaultModelParams;
|
||||
use codex_app_server_protocol::SetDefaultModelResponse;
|
||||
use codex_app_server_protocol::UploadFeedbackParams;
|
||||
use codex_app_server_protocol::UploadFeedbackResponse;
|
||||
use codex_app_server_protocol::UserInfoResponse;
|
||||
use codex_app_server_protocol::UserSavedConfig;
|
||||
use codex_backend_client::Client as BackendClient;
|
||||
@@ -85,6 +87,7 @@ use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::ExecApprovalRequestEvent;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_core::protocol::ReviewDecision;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_login::ServerOptions as LoginServerOptions;
|
||||
use codex_login::ShutdownHandle;
|
||||
use codex_login::run_login_server;
|
||||
@@ -136,6 +139,7 @@ pub(crate) struct CodexMessageProcessor {
|
||||
// Queue of pending interrupt requests per conversation. We reply when TurnAborted arrives.
|
||||
pending_interrupts: Arc<Mutex<HashMap<ConversationId, Vec<RequestId>>>>,
|
||||
pending_fuzzy_searches: Arc<Mutex<HashMap<String, Arc<AtomicBool>>>>,
|
||||
feedback: CodexFeedback,
|
||||
}
|
||||
|
||||
impl CodexMessageProcessor {
|
||||
@@ -145,6 +149,7 @@ impl CodexMessageProcessor {
|
||||
outgoing: Arc<OutgoingMessageSender>,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
config: Arc<Config>,
|
||||
feedback: CodexFeedback,
|
||||
) -> Self {
|
||||
Self {
|
||||
auth_manager,
|
||||
@@ -156,6 +161,7 @@ impl CodexMessageProcessor {
|
||||
active_login: Arc::new(Mutex::new(None)),
|
||||
pending_interrupts: Arc::new(Mutex::new(HashMap::new())),
|
||||
pending_fuzzy_searches: Arc::new(Mutex::new(HashMap::new())),
|
||||
feedback,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +281,9 @@ impl CodexMessageProcessor {
|
||||
} => {
|
||||
self.get_account_rate_limits(request_id).await;
|
||||
}
|
||||
ClientRequest::UploadFeedback { request_id, params } => {
|
||||
self.upload_feedback(request_id, params).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1418,6 +1427,77 @@ impl CodexMessageProcessor {
|
||||
let response = FuzzyFileSearchResponse { files: results };
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
async fn upload_feedback(&self, request_id: RequestId, params: UploadFeedbackParams) {
|
||||
let UploadFeedbackParams {
|
||||
classification,
|
||||
reason,
|
||||
conversation_id,
|
||||
include_logs,
|
||||
} = params;
|
||||
|
||||
let snapshot = self.feedback.snapshot(conversation_id);
|
||||
let thread_id = snapshot.thread_id.clone();
|
||||
|
||||
let validated_rollout_path = if include_logs {
|
||||
match conversation_id {
|
||||
Some(conv_id) => self.resolve_rollout_path(conv_id).await,
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let upload_result = tokio::task::spawn_blocking(move || {
|
||||
let rollout_path_ref = validated_rollout_path.as_deref();
|
||||
snapshot.upload_feedback(
|
||||
&classification,
|
||||
reason.as_deref(),
|
||||
include_logs,
|
||||
rollout_path_ref,
|
||||
)
|
||||
})
|
||||
.await;
|
||||
|
||||
let upload_result = match upload_result {
|
||||
Ok(result) => result,
|
||||
Err(join_err) => {
|
||||
let error = JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to upload feedback: {join_err}"),
|
||||
data: None,
|
||||
};
|
||||
self.outgoing.send_error(request_id, error).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match upload_result {
|
||||
Ok(()) => {
|
||||
let response = UploadFeedbackResponse { thread_id };
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
Err(err) => {
|
||||
let error = JSONRPCErrorError {
|
||||
code: INTERNAL_ERROR_CODE,
|
||||
message: format!("failed to upload feedback: {err}"),
|
||||
data: None,
|
||||
};
|
||||
self.outgoing.send_error(request_id, error).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn resolve_rollout_path(&self, conversation_id: ConversationId) -> Option<PathBuf> {
|
||||
match self
|
||||
.conversation_manager
|
||||
.get_conversation(conversation_id)
|
||||
.await
|
||||
{
|
||||
Ok(conv) => Some(conv.rollout_path()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn apply_bespoke_event_handling(
|
||||
|
||||
@@ -12,16 +12,19 @@ use crate::message_processor::MessageProcessor;
|
||||
use crate::outgoing_message::OutgoingMessage;
|
||||
use crate::outgoing_message::OutgoingMessageSender;
|
||||
use codex_app_server_protocol::JSONRPCMessage;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::io::BufReader;
|
||||
use tokio::io::{self};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::Level;
|
||||
use tracing::debug;
|
||||
use tracing::error;
|
||||
use tracing::info;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::Layer;
|
||||
use tracing_subscriber::filter::Targets;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
@@ -82,6 +85,8 @@ pub async fn run_main(
|
||||
std::io::Error::new(ErrorKind::InvalidData, format!("error loading config: {e}"))
|
||||
})?;
|
||||
|
||||
let feedback = CodexFeedback::new();
|
||||
|
||||
let otel =
|
||||
codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION")).map_err(|e| {
|
||||
std::io::Error::new(
|
||||
@@ -96,8 +101,15 @@ pub async fn run_main(
|
||||
.with_writer(std::io::stderr)
|
||||
.with_filter(EnvFilter::from_default_env());
|
||||
|
||||
let feedback_layer = tracing_subscriber::fmt::layer()
|
||||
.with_writer(feedback.make_writer())
|
||||
.with_ansi(false)
|
||||
.with_target(false)
|
||||
.with_filter(Targets::new().with_default(Level::TRACE));
|
||||
|
||||
let _ = tracing_subscriber::registry()
|
||||
.with(stderr_fmt)
|
||||
.with(feedback_layer)
|
||||
.with(otel.as_ref().map(|provider| {
|
||||
OpenTelemetryTracingBridge::new(&provider.logger).with_filter(
|
||||
tracing_subscriber::filter::filter_fn(codex_core::otel_init::codex_export_filter),
|
||||
@@ -112,6 +124,7 @@ pub async fn run_main(
|
||||
outgoing_message_sender,
|
||||
codex_linux_sandbox_exe,
|
||||
std::sync::Arc::new(config),
|
||||
feedback.clone(),
|
||||
);
|
||||
async move {
|
||||
while let Some(msg) = incoming_rx.recv().await {
|
||||
|
||||
@@ -17,6 +17,7 @@ use codex_core::ConversationManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::default_client::USER_AGENT_SUFFIX;
|
||||
use codex_core::default_client::get_codex_user_agent;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -33,6 +34,7 @@ impl MessageProcessor {
|
||||
outgoing: OutgoingMessageSender,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
config: Arc<Config>,
|
||||
feedback: CodexFeedback,
|
||||
) -> Self {
|
||||
let outgoing = Arc::new(outgoing);
|
||||
let auth_manager = AuthManager::shared(config.codex_home.clone(), false);
|
||||
@@ -46,6 +48,7 @@ impl MessageProcessor {
|
||||
outgoing.clone(),
|
||||
codex_linux_sandbox_exe,
|
||||
config,
|
||||
feedback,
|
||||
);
|
||||
|
||||
Self {
|
||||
|
||||
Reference in New Issue
Block a user