mirror of
https://github.com/openai/codex.git
synced 2026-04-29 02:41:12 +03:00
[codex] add otel tracing (#7844)
This commit is contained in:
@@ -9,15 +9,26 @@ use core_test_support::responses::ev_assistant_message;
|
||||
use core_test_support::responses::ev_completed;
|
||||
use core_test_support::responses::ev_custom_tool_call;
|
||||
use core_test_support::responses::ev_function_call;
|
||||
use core_test_support::responses::ev_local_shell_call;
|
||||
use core_test_support::responses::ev_message_item_added;
|
||||
use core_test_support::responses::ev_output_text_delta;
|
||||
use core_test_support::responses::ev_reasoning_item;
|
||||
use core_test_support::responses::ev_reasoning_summary_text_delta;
|
||||
use core_test_support::responses::ev_reasoning_text_delta;
|
||||
use core_test_support::responses::ev_response_created;
|
||||
use core_test_support::responses::mount_response_once;
|
||||
use core_test_support::responses::mount_sse_once;
|
||||
use core_test_support::responses::sse;
|
||||
use core_test_support::responses::sse_response;
|
||||
use core_test_support::responses::start_mock_server;
|
||||
use core_test_support::test_codex::TestCodex;
|
||||
use core_test_support::test_codex::test_codex;
|
||||
use core_test_support::wait_for_event;
|
||||
use std::sync::Mutex;
|
||||
use tracing_test::traced_test;
|
||||
|
||||
use core_test_support::responses::ev_local_shell_call;
|
||||
use tracing_subscriber::fmt::format::FmtSpan;
|
||||
use tracing_test::internal::MockWriter;
|
||||
|
||||
#[tokio::test]
|
||||
#[traced_test]
|
||||
@@ -437,6 +448,152 @@ async fn process_sse_emits_completed_telemetry() {
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handle_responses_span_records_response_kind_and_tool_name() {
|
||||
let buffer: &'static Mutex<Vec<u8>> = Box::leak(Box::new(Mutex::new(Vec::new())));
|
||||
let subscriber = tracing_subscriber::fmt()
|
||||
.with_level(true)
|
||||
.with_ansi(false)
|
||||
.with_span_events(FmtSpan::FULL)
|
||||
.with_writer(MockWriter::new(buffer))
|
||||
.finish();
|
||||
let _guard = tracing::subscriber::set_default(subscriber);
|
||||
|
||||
let server = start_mock_server().await;
|
||||
|
||||
mount_sse_once(
|
||||
&server,
|
||||
sse(vec![
|
||||
ev_function_call("function-call", "nonexistent", "{\"value\":1}"),
|
||||
ev_completed("done"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
mount_sse_once(
|
||||
&server,
|
||||
sse(vec![
|
||||
ev_assistant_message("msg-1", "tool handled"),
|
||||
ev_completed("done"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::GhostCommit);
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
}],
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
|
||||
|
||||
let logs = String::from_utf8(buffer.lock().unwrap().clone()).unwrap();
|
||||
|
||||
assert!(
|
||||
logs.contains("handle_responses{otel.name=\"function_call\"")
|
||||
&& logs.contains("tool_name=\"nonexistent\"")
|
||||
&& logs.contains("from=\"output_item_done\""),
|
||||
"missing handle_responses span with function call metadata\nlogs:\n{logs}"
|
||||
);
|
||||
assert!(
|
||||
logs.contains("handle_responses{otel.name=\"completed\""),
|
||||
"missing handle_responses span for completion\nlogs:\n{logs}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn record_responses_sets_span_fields_for_response_events() {
|
||||
let buffer: &'static Mutex<Vec<u8>> = Box::leak(Box::new(Mutex::new(Vec::new())));
|
||||
let subscriber = tracing_subscriber::fmt()
|
||||
.with_level(true)
|
||||
.with_ansi(false)
|
||||
.with_span_events(FmtSpan::FULL)
|
||||
.with_writer(MockWriter::new(buffer))
|
||||
.finish();
|
||||
let _guard = tracing::subscriber::set_default(subscriber);
|
||||
|
||||
let server = start_mock_server().await;
|
||||
|
||||
let sse_body = sse(vec![
|
||||
ev_response_created("resp-1"),
|
||||
ev_function_call("call-1", "fn", "{\"value\":1}"),
|
||||
ev_custom_tool_call("custom-1", "custom_tool", "{\"key\":\"value\"}"),
|
||||
ev_message_item_added("msg-added", "hi there"),
|
||||
ev_output_text_delta("delta"),
|
||||
ev_reasoning_summary_text_delta("summary-delta"),
|
||||
ev_reasoning_text_delta("raw-delta"),
|
||||
ev_function_call("call-1", "fn", "{\"key\":\"value\"}"),
|
||||
ev_custom_tool_call("custom-1", "custom_tool", "{\"key\":\"value\"}"),
|
||||
ev_assistant_message("msg-1", "agent"),
|
||||
ev_reasoning_item("reasoning-1", &["summary"], &[]),
|
||||
ev_completed("resp-1"),
|
||||
]);
|
||||
|
||||
mount_response_once(&server, sse_response(sse_body)).await;
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::GhostCommit);
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
}],
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await;
|
||||
|
||||
let logs = String::from_utf8(buffer.lock().unwrap().clone()).unwrap();
|
||||
|
||||
let expected = [
|
||||
("created", None::<&str>, None::<&str>),
|
||||
("rate_limits", None, None),
|
||||
("function_call", Some("output_item_added"), Some("fn")),
|
||||
("message_from_assistant", Some("output_item_done"), None),
|
||||
("reasoning", Some("output_item_done"), None),
|
||||
("text_delta", None, None),
|
||||
("reasoning_summary_delta", None, None),
|
||||
("reasoning_content_delta", None, None),
|
||||
("completed", None, None),
|
||||
];
|
||||
|
||||
for (name, from, tool_name) in expected {
|
||||
assert!(
|
||||
logs.contains(&format!("handle_responses{{otel.name=\"{name}\"")),
|
||||
"missing otel.name={name}\nlogs:\n{logs}"
|
||||
);
|
||||
if let Some(from) = from {
|
||||
assert!(
|
||||
logs.contains(&format!("from=\"{from}\"")),
|
||||
"missing from={from} for {name}\nlogs:\n{logs}"
|
||||
);
|
||||
}
|
||||
if let Some(tool_name) = tool_name {
|
||||
assert!(
|
||||
logs.contains(&format!("tool_name=\"{tool_name}\"")),
|
||||
"missing tool_name={tool_name} for {name}\nlogs:\n{logs}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[traced_test]
|
||||
async fn handle_response_item_records_tool_result_for_custom_tool_call() {
|
||||
|
||||
Reference in New Issue
Block a user