From 85af9d7078ceeea8c4a659e4249d0dc4688631a7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Jun 2026 10:47:56 +0200 Subject: [PATCH] Add safe chat reasoning disclosure --- ...s-chat-agent-UI-with-AI-Elements-phases.md | 6 ++- src/components/chat/AgentChat.test.tsx | 39 ++++++++++++++++++- src/components/chat/AgentChat.tsx | 37 ++++++++++-------- src/components/chat/agentChatModel.ts | 16 ++++++++ 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/backlog/tasks/task-10 - Modernize-savings-chat-agent-UI-with-AI-Elements-phases.md b/backlog/tasks/task-10 - Modernize-savings-chat-agent-UI-with-AI-Elements-phases.md index c2aa1fc..794bc59 100644 --- a/backlog/tasks/task-10 - Modernize-savings-chat-agent-UI-with-AI-Elements-phases.md +++ b/backlog/tasks/task-10 - Modernize-savings-chat-agent-UI-with-AI-Elements-phases.md @@ -4,7 +4,7 @@ title: Modernize savings chat agent UI with AI Elements phases status: In Progress assignee: [] created_date: '2026-06-16 08:38' -updated_date: '2026-06-16 08:41' +updated_date: '2026-06-16 08:46' labels: [] dependencies: [] priority: high @@ -20,7 +20,7 @@ Implement the planned AI Elements-inspired savings chat agent UI in three sequen ## Acceptance Criteria - [x] #1 Phase 1 replaces the basic chat surface with reusable conversation, message, prompt input, and tool trace UI primitives -- [ ] #2 Phase 2 adds a safe reasoning/work-progress disclosure derived from existing tool traces, without exposing hidden chain-of-thought +- [x] #2 Phase 2 adds a safe reasoning/work-progress disclosure derived from existing tool traces, without exposing hidden chain-of-thought - [ ] #3 Phase 3 adds structured source/citation support through stored assistant metadata and visible UI affordances - [ ] #4 Each phase is covered by failing-first tests, verified after implementation, and committed separately @@ -41,4 +41,6 @@ Implement the planned AI Elements-inspired savings chat agent UI in three sequen Phase 1 complete: added AgentChat primitives for conversation, message rendering, prompt input, and tool trace disclosure; integrated SavingsChatPage. Verification: npx vitest src/components/chat/AgentChat.test.tsx --run, npx eslint targeted chat/page files, npm run build (Vite chunk-size warning only). + +Phase 2 complete locally: added safe work-progress/reasoning disclosure derived from toolTrace result summaries, removed raw inputSummary display from the disclosure, and added active progress state while a response is pending. Verification: npx vitest src/components/chat/AgentChat.test.tsx --run (9 tests), targeted eslint, npm run build (Vite chunk-size warning only). Spec subagent review approved. diff --git a/src/components/chat/AgentChat.test.tsx b/src/components/chat/AgentChat.test.tsx index 14f8284..7a4b73b 100644 --- a/src/components/chat/AgentChat.test.tsx +++ b/src/components/chat/AgentChat.test.tsx @@ -6,7 +6,7 @@ import { AgentPromptInput, type AgentChatMessage, } from "./AgentChat"; -import { getToolTraceSummary } from "./agentChatModel"; +import { buildReasoningSteps, getToolTraceSummary } from "./agentChatModel"; const assistantMessage: AgentChatMessage = { id: "assistant-1", @@ -111,3 +111,40 @@ describe("AgentChat phase 1 components", () => { expect(markup).not.toContain("Werkzeuge verwendet"); }); }); + +describe("AgentChat phase 2 reasoning disclosure", () => { + test("builds safe reasoning steps from tool traces without exposing raw inputs", () => { + const steps = buildReasoningSteps(assistantMessage.toolTrace); + + expect(steps).toEqual([ + { + label: "summarize_transactions", + description: "12 Umsaetze zusammengefasst", + status: "complete", + }, + { + label: "list_transactions", + description: "1 Treffer", + status: "complete", + }, + ]); + expect(JSON.stringify(steps)).not.toContain("Mai 2026"); + }); + + test("renders an assistant work-progress disclosure from tool traces", () => { + const markup = renderToStaticMarkup(); + + expect(markup).toContain("So wurde gearbeitet"); + expect(markup).toContain("summarize_transactions"); + expect(markup).toContain("12 Umsaetze zusammengefasst"); + }); + + test("marks the current agent step as active while submitting", () => { + const markup = renderToStaticMarkup( + , + ); + + expect(markup).toContain("Antwort wird vorbereitet"); + expect(markup).toContain("data-status=\"active\""); + }); +}); diff --git a/src/components/chat/AgentChat.tsx b/src/components/chat/AgentChat.tsx index 767b7ac..026fef2 100644 --- a/src/components/chat/AgentChat.tsx +++ b/src/components/chat/AgentChat.tsx @@ -2,15 +2,16 @@ import { type ChangeEvent, type FormEvent, type HTMLAttributes, - type ReactNode, type Ref, } from "react"; import { Loader2, Send, Wrench } from "lucide-react"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { + buildReasoningSteps, getToolTraceSummary, type AgentChatMessage, + type AgentReasoningStep, type AgentToolTrace, } from "./agentChatModel"; export type { AgentChatMessage, AgentToolTrace } from "./agentChatModel"; @@ -89,39 +90,43 @@ export function AgentMessage({ message, className, ...props }: AgentMessageProps } function AgentToolTracePanel({ toolTrace }: { toolTrace: AgentToolTrace[] }) { + const steps = buildReasoningSteps(toolTrace); + return (
- {getToolTraceSummary(toolTrace)} + So wurde gearbeitet ({getToolTraceSummary(toolTrace)})
- {toolTrace.map((tool, toolIndex) => ( -
-

{tool.name}

- - -
+ {steps.map((step, stepIndex) => ( + ))}
); } -function ToolTraceLine({ label, value }: { label: ReactNode; value: ReactNode }) { +function ReasoningStep({ step }: { step: AgentReasoningStep }) { return ( -

- {label}: - {value} -

+
+

{step.label}

+

{step.description}

+
); } function AgentThinkingIndicator() { return ( -
- - Denk mit der KI nach... +
+
+ + Denk mit der KI nach... +
+
+

Antwort wird vorbereitet

+

Der Agent prueft den aktuellen Finanzkontext.

+
); } diff --git a/src/components/chat/agentChatModel.ts b/src/components/chat/agentChatModel.ts index 5434dff..58a7960 100644 --- a/src/components/chat/agentChatModel.ts +++ b/src/components/chat/agentChatModel.ts @@ -11,7 +11,23 @@ export type AgentChatMessage = { toolTrace?: AgentToolTrace[]; }; +export type AgentReasoningStep = { + label: string; + description: string; + status: "complete" | "active" | "pending"; +}; + export function getToolTraceSummary(toolTrace: AgentToolTrace[] | undefined) { if (!toolTrace || toolTrace.length === 0) return "Keine Werkzeuge"; return `${toolTrace.length} ${toolTrace.length === 1 ? "Werkzeug" : "Werkzeuge"} verwendet`; } + +export function buildReasoningSteps(toolTrace: AgentToolTrace[] | undefined): AgentReasoningStep[] { + if (!toolTrace || toolTrace.length === 0) return []; + + return toolTrace.map((tool) => ({ + label: tool.name, + description: tool.resultSummary, + status: "complete", + })); +}