mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(lens): show the first user message as the run input (#44958)
* fix(traces): use the first user message for the run input preview * test(traces): cover first user message as the input preview * test(traces): check fixture previews against the first user message * fix(lens): show the whole input preview on one line in the runs table * test(lens): cover multi-line input previews in the runs table
This commit is contained in:
parent
9a3f000c9b
commit
d8bc2b78e4
5 changed files with 45 additions and 7 deletions
|
|
@ -422,11 +422,10 @@ pub(super) fn event_payload(events: &[(bool, Value)], output: bool) -> Option<St
|
|||
(!values.is_empty()).then(|| canonical(&encode(&values)))
|
||||
}
|
||||
|
||||
/// The latest user message with text.
|
||||
/// The first user message with text.
|
||||
pub(super) fn preview(messages: &[Message]) -> String {
|
||||
messages
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|message| message.role == "user" && !message.content.is_empty())
|
||||
.map_or("", |message| message.content.as_str())
|
||||
.chars()
|
||||
|
|
@ -434,7 +433,7 @@ pub(super) fn preview(messages: &[Message]) -> String {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// The latest user message when `input` is a conversation, else the input itself.
|
||||
/// The first user message when `input` is a conversation, else the input itself.
|
||||
pub(super) fn input_preview(input: &str) -> String {
|
||||
match serde_json::from_str::<Value>(input)
|
||||
.ok()
|
||||
|
|
@ -597,7 +596,7 @@ mod tests {
|
|||
use serde_json::Value;
|
||||
|
||||
#[rstest]
|
||||
#[case::latest_user(r#"{"messages":[{"role":"user","content":"first"},{"role":"assistant","content":"reply"},{"role":"user","content":"last"}]}"#, Some("last"))]
|
||||
#[case::first_user(r#"{"messages":[{"role":"user","content":"first"},{"role":"assistant","content":"reply"},{"role":"user","content":"last"}]}"#, Some("first"))]
|
||||
#[case::malformed("not-json", None)]
|
||||
#[case::missing("{}", None)]
|
||||
#[case::not_messages(r#"{"messages":[{"role":"user"}]}"#, None)]
|
||||
|
|
|
|||
|
|
@ -502,6 +502,25 @@ fn existing_formats_win_over_new_formats(span: Span, #[case] kind: &str) {
|
|||
assert_eq!(decoded.normalized.observation_type, ObservationType::Llm);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn input_preview_is_the_first_user_message(span: Span) {
|
||||
let conversation = json!([
|
||||
{"role": "system", "content": "sys"},
|
||||
{"role": "user", "content": "initial question"},
|
||||
{"role": "assistant", "content": "answer"},
|
||||
{"role": "user", "content": "follow up"},
|
||||
])
|
||||
.to_string();
|
||||
let decoded = decode(
|
||||
span,
|
||||
"custom",
|
||||
&[("gen_ai.input.messages", &conversation)],
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(decoded.normalized.input_preview, "initial question");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::messages(&[("gen_ai.input.messages", r#"[{"role":"user","content":"modern"}]"#)], "modern")]
|
||||
#[case::indexed(&[("gen_ai.prompt.0.role", "user"), ("gen_ai.prompt.0.content", "indexed")], "indexed")]
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ fn assert_invariants(span: &DecodedSpan) {
|
|||
}
|
||||
assert!(normalized.input_preview.chars().count() <= 240);
|
||||
if let Ok(Value::Array(messages)) = serde_json::from_str(&normalized.input) {
|
||||
let user = messages.iter().rev().find_map(|message| {
|
||||
let user = messages.iter().find_map(|message| {
|
||||
(message.get("role")?.as_str()? == "user")
|
||||
.then(|| {
|
||||
message
|
||||
|
|
|
|||
|
|
@ -165,6 +165,26 @@ describe("AgentTracesTable cost cell", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("AgentTracesTable input cell", () => {
|
||||
it("shows the whole preview on one line instead of only its first line", () => {
|
||||
const template = (traceList as TracePage).data[0] as TraceSummary;
|
||||
renderWithProviders(
|
||||
inList(
|
||||
<AgentTracesTable
|
||||
traces={[{ ...template, input_preview: "CURRENT USER REQUEST:\n\n- add feedback to Lens" }]}
|
||||
findings={new Map()}
|
||||
isLoading={false}
|
||||
error={null}
|
||||
hasMore={false}
|
||||
onLoadMore={vi.fn()}
|
||||
onSetUpTracing={vi.fn()}
|
||||
/>,
|
||||
),
|
||||
);
|
||||
expect(screen.getByText("CURRENT USER REQUEST: - add feedback to Lens")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("AgentTracesTable column picker", () => {
|
||||
const runs = (traceList as TracePage).data as TraceSummary[];
|
||||
const renderRuns = () =>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ function CostCell({ run }: { run: TraceSummary }) {
|
|||
);
|
||||
}
|
||||
|
||||
const firstLine = (text: string): string => text.split("\n")[0] ?? text;
|
||||
const singleLine = (text: string): string => text.replace(/\s+/g, " ").trim();
|
||||
const runKey = (run: TraceSummary): string => run.trace_ref || run.trace_id;
|
||||
|
||||
const PREFETCH_MARGIN = "0px 0px 480px 0px";
|
||||
|
|
@ -96,7 +96,7 @@ function InputCell({ run }: { run: TraceSummary }) {
|
|||
return (
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span className="truncate text-foreground">
|
||||
{firstLine(previewText(run.input_preview)) || traceDisplayName(run)}
|
||||
{singleLine(previewText(run.input_preview)) || traceDisplayName(run)}
|
||||
</span>
|
||||
{run.resolution_limited && (
|
||||
<span
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue