Align assistant metadata display with message content
This commit is contained in:
@@ -14,6 +14,7 @@ interface MessageItemProps {
|
|||||||
orderedParts: ClientPart[]
|
orderedParts: ClientPart[]
|
||||||
onRevert?: (messageId: string) => void
|
onRevert?: (messageId: string) => void
|
||||||
onFork?: (messageId?: string) => void
|
onFork?: (messageId?: string) => void
|
||||||
|
showAgentMeta?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MessageItem(props: MessageItemProps) {
|
export default function MessageItem(props: MessageItemProps) {
|
||||||
@@ -166,17 +167,26 @@ export default function MessageItem(props: MessageItemProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div class={containerClass()}>
|
<div class={containerClass()}>
|
||||||
<div class={`flex justify-between items-center gap-2.5 ${isUser() ? "pb-0.5" : "pb-0"}`}>
|
<div class={`flex justify-between items-center gap-2.5 ${isUser() ? "pb-0.5" : "pb-0"}`}>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<Show when={isUser()}>
|
<Show when={isUser()}>
|
||||||
<span class="font-semibold text-xs text-[var(--message-user-border)]">You</span>
|
<span class="font-semibold text-xs text-[var(--message-user-border)]">You</span>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={!isUser()}>
|
<Show when={!isUser()}>
|
||||||
<span class="text-xs text-[var(--message-assistant-border)]">Assistant</span>
|
<div class="flex flex-wrap items-center gap-2 text-xs text-[var(--message-assistant-border)]">
|
||||||
</Show>
|
<span class="font-semibold">Assistant</span>
|
||||||
</div>
|
<Show when={props.showAgentMeta && (agentIdentifier() || modelIdentifier())}>
|
||||||
<div class="flex items-center gap-2">
|
<span class="message-step-meta-inline">
|
||||||
|
<Show when={agentIdentifier()}>{(value) => <span class="font-medium text-[var(--message-assistant-border)]">Agent: {value()}</span>}</Show>
|
||||||
|
<Show when={modelIdentifier()}>{(value) => <span class="font-medium text-[var(--message-assistant-border)]">Model: {value()}</span>}</Show>
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
|
||||||
<Show when={isUser() && props.onRevert}>
|
<Show when={isUser() && props.onRevert}>
|
||||||
<button
|
<button
|
||||||
class="bg-transparent border border-[var(--border-base)] text-[var(--text-muted)] cursor-pointer px-3 py-0.5 rounded text-xs font-semibold leading-none transition-all duration-200 flex items-center justify-center h-6 hover:bg-[var(--surface-hover)] hover:border-[var(--accent-primary)] hover:text-[var(--accent-primary)] active:scale-95"
|
class="bg-transparent border border-[var(--border-base)] text-[var(--text-muted)] cursor-pointer px-3 py-0.5 rounded text-xs font-semibold leading-none transition-all duration-200 flex items-center justify-center h-6 hover:bg-[var(--surface-hover)] hover:border-[var(--accent-primary)] hover:text-[var(--accent-primary)] active:scale-95"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import MessageItem from "./message-item"
|
|||||||
import ToolCall from "./tool-call"
|
import ToolCall from "./tool-call"
|
||||||
import Kbd from "./kbd"
|
import Kbd from "./kbd"
|
||||||
import type { MessageInfo, ClientPart } from "../types/message"
|
import type { MessageInfo, ClientPart } from "../types/message"
|
||||||
|
import { partHasRenderableText } from "../types/message"
|
||||||
import { getSessionInfo, sessions, setActiveParentSession, setActiveSession } from "../stores/sessions"
|
import { getSessionInfo, sessions, setActiveParentSession, setActiveSession } from "../stores/sessions"
|
||||||
import { showCommandPalette } from "../stores/command-palette"
|
import { showCommandPalette } from "../stores/command-palette"
|
||||||
import { messageStoreBus } from "../stores/message-v2/bus"
|
import { messageStoreBus } from "../stores/message-v2/bus"
|
||||||
@@ -120,6 +121,7 @@ interface ContentDisplayItem {
|
|||||||
parts: ClientPart[]
|
parts: ClientPart[]
|
||||||
messageInfo?: MessageInfo
|
messageInfo?: MessageInfo
|
||||||
isQueued: boolean
|
isQueued: boolean
|
||||||
|
showAgentMeta?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolDisplayItem {
|
interface ToolDisplayItem {
|
||||||
@@ -144,6 +146,7 @@ type ReasoningDisplayItem = {
|
|||||||
key: string
|
key: string
|
||||||
part: ClientPart
|
part: ClientPart
|
||||||
messageInfo?: MessageInfo
|
messageInfo?: MessageInfo
|
||||||
|
showAgentMeta?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageBlockItem = ContentDisplayItem | ToolDisplayItem | StepDisplayItem | ReasoningDisplayItem
|
type MessageBlockItem = ContentDisplayItem | ToolDisplayItem | StepDisplayItem | ReasoningDisplayItem
|
||||||
@@ -280,11 +283,16 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
const items: MessageBlockItem[] = []
|
const items: MessageBlockItem[] = []
|
||||||
let segmentIndex = 0
|
let segmentIndex = 0
|
||||||
let pendingParts: ClientPart[] = []
|
let pendingParts: ClientPart[] = []
|
||||||
|
let agentMetaAttached = record.role !== "assistant"
|
||||||
|
|
||||||
const flushContent = () => {
|
const flushContent = () => {
|
||||||
if (pendingParts.length === 0) return
|
if (pendingParts.length === 0) return
|
||||||
const segmentKey = makeInstanceCacheKey(instanceId, `${record.id}:segment:${segmentIndex}`)
|
const segmentKey = makeInstanceCacheKey(instanceId, `${record.id}:segment:${segmentIndex}`)
|
||||||
segmentIndex += 1
|
segmentIndex += 1
|
||||||
|
const shouldShowAgentMeta =
|
||||||
|
record.role === "assistant" &&
|
||||||
|
!agentMetaAttached &&
|
||||||
|
pendingParts.some((part) => partHasRenderableText(part))
|
||||||
let cached = messageItemCache.get(segmentKey)
|
let cached = messageItemCache.get(segmentKey)
|
||||||
if (!cached) {
|
if (!cached) {
|
||||||
cached = {
|
cached = {
|
||||||
@@ -294,6 +302,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
parts: pendingParts.slice(),
|
parts: pendingParts.slice(),
|
||||||
messageInfo,
|
messageInfo,
|
||||||
isQueued,
|
isQueued,
|
||||||
|
showAgentMeta: shouldShowAgentMeta,
|
||||||
}
|
}
|
||||||
messageItemCache.set(segmentKey, cached)
|
messageItemCache.set(segmentKey, cached)
|
||||||
} else {
|
} else {
|
||||||
@@ -301,6 +310,10 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
cached.parts = pendingParts.slice()
|
cached.parts = pendingParts.slice()
|
||||||
cached.messageInfo = messageInfo
|
cached.messageInfo = messageInfo
|
||||||
cached.isQueued = isQueued
|
cached.isQueued = isQueued
|
||||||
|
cached.showAgentMeta = shouldShowAgentMeta
|
||||||
|
}
|
||||||
|
if (shouldShowAgentMeta) {
|
||||||
|
agentMetaAttached = true
|
||||||
}
|
}
|
||||||
items.push(cached)
|
items.push(cached)
|
||||||
usedMessageKeys.add(segmentKey)
|
usedMessageKeys.add(segmentKey)
|
||||||
@@ -341,8 +354,6 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
|
|
||||||
if (part.type === "step-start") {
|
if (part.type === "step-start") {
|
||||||
flushContent()
|
flushContent()
|
||||||
const key = makeInstanceCacheKey(instanceId, `${record.id}:${part.id ?? partIndex}:${part.type}`)
|
|
||||||
items.push({ type: part.type, key, part, messageInfo })
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +370,11 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
flushContent()
|
flushContent()
|
||||||
if (showThinking && reasoningHasRenderableContent(part)) {
|
if (showThinking && reasoningHasRenderableContent(part)) {
|
||||||
const key = makeInstanceCacheKey(instanceId, `${record.id}:${part.id ?? partIndex}:reasoning`)
|
const key = makeInstanceCacheKey(instanceId, `${record.id}:${part.id ?? partIndex}:reasoning`)
|
||||||
items.push({ type: "reasoning", key, part, messageInfo })
|
const showAgentMeta = record.role === "assistant" && !agentMetaAttached
|
||||||
|
if (showAgentMeta) {
|
||||||
|
agentMetaAttached = true
|
||||||
|
}
|
||||||
|
items.push({ type: "reasoning", key, part, messageInfo, showAgentMeta })
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -622,6 +637,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
instanceId={props.instanceId}
|
instanceId={props.instanceId}
|
||||||
sessionId={props.sessionId}
|
sessionId={props.sessionId}
|
||||||
isQueued={(item as ContentDisplayItem).isQueued}
|
isQueued={(item as ContentDisplayItem).isQueued}
|
||||||
|
showAgentMeta={(item as ContentDisplayItem).showAgentMeta}
|
||||||
onRevert={props.onRevert}
|
onRevert={props.onRevert}
|
||||||
onFork={props.onFork}
|
onFork={props.onFork}
|
||||||
/>
|
/>
|
||||||
@@ -699,6 +715,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
messageInfo={(item as ReasoningDisplayItem).messageInfo}
|
messageInfo={(item as ReasoningDisplayItem).messageInfo}
|
||||||
instanceId={props.instanceId}
|
instanceId={props.instanceId}
|
||||||
sessionId={props.sessionId}
|
sessionId={props.sessionId}
|
||||||
|
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
@@ -868,6 +885,7 @@ interface ReasoningCardProps {
|
|||||||
messageInfo?: MessageInfo
|
messageInfo?: MessageInfo
|
||||||
instanceId: string
|
instanceId: string
|
||||||
sessionId: string
|
sessionId: string
|
||||||
|
showAgentMeta?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReasoningCard(props: ReasoningCardProps) {
|
function ReasoningCard(props: ReasoningCardProps) {
|
||||||
@@ -879,6 +897,21 @@ function ReasoningCard(props: ReasoningCardProps) {
|
|||||||
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
|
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const agentIdentifier = () => {
|
||||||
|
const info = props.messageInfo
|
||||||
|
if (!info || info.role !== "assistant") return ""
|
||||||
|
return info.mode || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelIdentifier = () => {
|
||||||
|
const info = props.messageInfo
|
||||||
|
if (!info || info.role !== "assistant") return ""
|
||||||
|
const modelID = info.modelID || ""
|
||||||
|
const providerID = info.providerID || ""
|
||||||
|
if (modelID && providerID) return `${providerID}/${modelID}`
|
||||||
|
return modelID
|
||||||
|
}
|
||||||
|
|
||||||
const reasoningText = () => {
|
const reasoningText = () => {
|
||||||
const part = props.part as any
|
const part = props.part as any
|
||||||
if (!part) return ""
|
if (!part) return ""
|
||||||
@@ -925,7 +958,15 @@ function ReasoningCard(props: ReasoningCardProps) {
|
|||||||
aria-expanded={expanded()}
|
aria-expanded={expanded()}
|
||||||
aria-label={expanded() ? "Collapse thinking" : "Expand thinking"}
|
aria-label={expanded() ? "Collapse thinking" : "Expand thinking"}
|
||||||
>
|
>
|
||||||
<span class="message-reasoning-label">Thinking</span>
|
<span class="message-reasoning-label flex flex-wrap items-center gap-2">
|
||||||
|
<span>Thinking</span>
|
||||||
|
<Show when={props.showAgentMeta && (agentIdentifier() || modelIdentifier())}>
|
||||||
|
<span class="message-step-meta-inline">
|
||||||
|
<Show when={agentIdentifier()}>{(value) => <span class="font-medium text-[var(--message-assistant-border)]">Agent: {value()}</span>}</Show>
|
||||||
|
<Show when={modelIdentifier()}>{(value) => <span class="font-medium text-[var(--message-assistant-border)]">Model: {value()}</span>}</Show>
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
|
</span>
|
||||||
<span class="message-reasoning-meta">
|
<span class="message-reasoning-meta">
|
||||||
<span class="message-reasoning-indicator">{expanded() ? "Hide" : "View"}</span>
|
<span class="message-reasoning-indicator">{expanded() ? "Hide" : "View"}</span>
|
||||||
<span class="message-reasoning-time">{timestamp()}</span>
|
<span class="message-reasoning-time">{timestamp()}</span>
|
||||||
|
|||||||
@@ -172,7 +172,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message-step-meta-inline {
|
.message-step-meta-inline {
|
||||||
@apply flex flex-wrap gap-2 text-[11px];
|
@apply inline-flex flex-wrap items-center gap-2 text-[11px] font-medium;
|
||||||
color: var(--message-assistant-border);
|
color: var(--message-assistant-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user