Unify chat evidence display
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
id: TASK-12
|
||||||
|
title: Unify savings chat tools and sources UI
|
||||||
|
status: In Progress
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-06-16 09:07'
|
||||||
|
updated_date: '2026-06-16 09:09'
|
||||||
|
labels: []
|
||||||
|
dependencies: []
|
||||||
|
priority: high
|
||||||
|
ordinal: 12000
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Replace duplicated Tools and Quellen panels in savings chat assistant messages with one combined evidence block when tool traces and generated sources describe the same data basis.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Assistant messages with both toolTrace and matching sources render a single Nachweis & Arbeitsweg block
|
||||||
|
- [x] #2 The combined block exposes citation source ids on the evidence rows so inline markers still point at the same evidence
|
||||||
|
- [x] #3 Sources-only assistant messages can still render a Quellen block for genuinely external sources
|
||||||
|
- [x] #4 Focused component tests, targeted lint, and build pass
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add regression tests for assistant messages where toolTrace and sources describe the same evidence.
|
||||||
|
2. Replace the separate Tools + Quellen rendering path with a single Nachweis & Arbeitsweg evidence panel when toolTrace exists.
|
||||||
|
3. Keep the Quellen-only fallback for messages that have sources without tool traces.
|
||||||
|
4. Verify focused component tests, targeted lint, and production build.
|
||||||
|
5. Commit the UI refinement; keep task In Progress until user confirms manual behavior.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Unified generated tool traces and generated sources in AgentChat. Assistant messages with toolTrace now render one Nachweis & Arbeitsweg disclosure and attach source ids to the evidence rows, so inline citation markers still resolve to the same evidence. Sources-only messages still render the Quellen fallback for external/document sources. Verification passed: npx vitest src/components/chat/AgentChat.test.tsx --run (13 tests), targeted eslint, npm run build (existing Vite chunk-size warning only).
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -150,7 +150,7 @@ describe("AgentChat phase 2 reasoning disclosure", () => {
|
|||||||
test("renders an assistant work-progress disclosure from tool traces", () => {
|
test("renders an assistant work-progress disclosure from tool traces", () => {
|
||||||
const markup = renderToStaticMarkup(<AgentMessage message={assistantMessage} />);
|
const markup = renderToStaticMarkup(<AgentMessage message={assistantMessage} />);
|
||||||
|
|
||||||
expect(markup).toContain("So wurde gearbeitet");
|
expect(markup).toContain("Nachweis & Arbeitsweg");
|
||||||
expect(markup).toContain("summarize_transactions");
|
expect(markup).toContain("summarize_transactions");
|
||||||
expect(markup).toContain("12 Umsaetze zusammengefasst");
|
expect(markup).toContain("12 Umsaetze zusammengefasst");
|
||||||
});
|
});
|
||||||
@@ -206,4 +206,38 @@ describe("AgentChat phase 3 sources and inline citations", () => {
|
|||||||
expect(markup).toContain("[1]");
|
expect(markup).toContain("[1]");
|
||||||
expect(markup).toContain("data-source-id=\"tool-1\"");
|
expect(markup).toContain("data-source-id=\"tool-1\"");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("combines matching tool traces and sources into one evidence block", () => {
|
||||||
|
const markup = renderToStaticMarkup(
|
||||||
|
<AgentMessage
|
||||||
|
message={{
|
||||||
|
id: "assistant-evidence",
|
||||||
|
role: "assistant",
|
||||||
|
content: "Es gab 24 Buchungen [1].",
|
||||||
|
toolTrace: [
|
||||||
|
{
|
||||||
|
name: "get_transactions",
|
||||||
|
inputSummary: "Kategorie Rahmenkredite",
|
||||||
|
resultSummary: "24 Umsaetze, Saldo -13483.41€, vollstaendig",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
id: "tool-1",
|
||||||
|
title: "get_transactions",
|
||||||
|
description: "24 Umsaetze, Saldo -13483.41€, vollstaendig",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
citations: [{ marker: "1", sourceId: "tool-1" }],
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(markup).toContain("Nachweis & Arbeitsweg");
|
||||||
|
expect(markup).toContain("1 Werkzeug verwendet");
|
||||||
|
expect(markup).toContain("get_transactions");
|
||||||
|
expect(markup).toContain("24 Umsaetze, Saldo -13483.41€, vollstaendig");
|
||||||
|
expect(markup).toContain("data-source-id=\"tool-1\"");
|
||||||
|
expect(markup).not.toContain(">Quellen<");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -89,9 +89,9 @@ export function AgentMessage({ message, className, ...props }: AgentMessageProps
|
|||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
{!isUser && message.toolTrace && message.toolTrace.length > 0 && (
|
{!isUser && message.toolTrace && message.toolTrace.length > 0 && (
|
||||||
<AgentToolTracePanel toolTrace={message.toolTrace} />
|
<AgentEvidencePanel sources={message.sources} toolTrace={message.toolTrace} />
|
||||||
)}
|
)}
|
||||||
{!isUser && message.sources && message.sources.length > 0 && (
|
{!isUser && (!message.toolTrace || message.toolTrace.length === 0) && message.sources && message.sources.length > 0 && (
|
||||||
<AgentSources sources={message.sources} />
|
<AgentSources sources={message.sources} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -154,27 +154,47 @@ function AgentSources({ sources }: { sources: AgentSource[] }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AgentToolTracePanel({ toolTrace }: { toolTrace: AgentToolTrace[] }) {
|
function AgentEvidencePanel({
|
||||||
|
sources,
|
||||||
|
toolTrace,
|
||||||
|
}: {
|
||||||
|
sources?: AgentSource[];
|
||||||
|
toolTrace: AgentToolTrace[];
|
||||||
|
}) {
|
||||||
const steps = buildReasoningSteps(toolTrace);
|
const steps = buildReasoningSteps(toolTrace);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<details className="mt-3 rounded-md border bg-muted/30 px-2 py-1.5">
|
<details className="mt-3 rounded-md border bg-muted/30 px-2 py-1.5">
|
||||||
<summary className="flex cursor-pointer list-none items-center gap-2 text-xs font-medium text-muted-foreground">
|
<summary className="flex cursor-pointer list-none items-center gap-2 text-xs font-medium text-muted-foreground">
|
||||||
<Wrench className="h-3.5 w-3.5" />
|
<Wrench className="h-3.5 w-3.5" />
|
||||||
So wurde gearbeitet ({getToolTraceSummary(toolTrace)})
|
Nachweis & Arbeitsweg ({getToolTraceSummary(toolTrace)})
|
||||||
</summary>
|
</summary>
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
{steps.map((step, stepIndex) => (
|
{steps.map((step, stepIndex) => (
|
||||||
<ReasoningStep key={`${step.label}-${stepIndex}`} step={step} />
|
<ReasoningStep
|
||||||
|
key={`${step.label}-${stepIndex}`}
|
||||||
|
sourceId={sources?.[stepIndex]?.id}
|
||||||
|
step={step}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReasoningStep({ step }: { step: AgentReasoningStep }) {
|
function ReasoningStep({
|
||||||
|
sourceId,
|
||||||
|
step,
|
||||||
|
}: {
|
||||||
|
sourceId?: string;
|
||||||
|
step: AgentReasoningStep;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="rounded-md bg-background/80 p-2 text-xs" data-status={step.status}>
|
<div
|
||||||
|
className="rounded-md bg-background/80 p-2 text-xs"
|
||||||
|
data-source-id={sourceId}
|
||||||
|
data-status={step.status}
|
||||||
|
>
|
||||||
<p className="font-medium text-foreground">{step.label}</p>
|
<p className="font-medium text-foreground">{step.label}</p>
|
||||||
<p className="mt-1 text-muted-foreground">{step.description}</p>
|
<p className="mt-1 text-muted-foreground">{step.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user