Repository navigation
Show streamed Expert tool calls, reasoning and progress messages - #8895
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8895 +/- ##
==========================================
+ Coverage 79.08% 79.29% +0.20%
==========================================
Files 476 476
Lines 25890 25912 +22
Branches 6886 6896 +10
==========================================
+ Hits 20475 20546 +71
+ Misses 5415 5366 -49
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
n-lark
left a comment
There was a problem hiding this comment.
Hey just a couple things good work. Two things that look deliberate but don't match the issues this closes:
- #549 asks for the tool name in the header with the reason inside
- #551 asks for the reasoning block to be open while text arrives and collapse when it ends.
Fine to keep the implementation as is. Just worth a note or updating the issues.
An update replaced the item with its new seq but left it in place, so the bubble was no longer sorted and a later insert could land above it. The item now keeps its first seq, the newest update's seq is tracked separately for the late-update check, and fields an update leaves out are kept.
The header is now a button with aria-expanded, so it can be focused and toggled from the keyboard. A collapsible prop renders a static header with no chevron or toggle when there is nothing to expand.
Tool cards show input and output in the shared JsonViewer instead of their own highlighter and toggles. A card with no reason, error or data has nothing to expand, so it shows no chevron.
|
Thanks! Updated FlowFuse/engineering#549 and FlowFuse/engineering#551 to match what shipped: the reason heads the tool card, and the reasoning block stays collapsed with a one-line preview so the chat does not jump while it streams. |
Adds an align prop and a plain kind to ff-button so it can render a left-aligned header with no background or padding.
Steve-Mcl
left a comment
There was a problem hiding this comment.
when a turn leaves _inFlightRequests without a final answer (Stop, handleMqttError, Start over), set done on its bubble's open items, or add a stopped marker.
Alternatively, have ReasoningBlock only show "Thinking" while the turn is live. It's a small store change plus one test. Below is a screenshot of what happens if convo is stopped during thinking (note the partial streamed character "I" at the end:
A few smaller things, none blocking:
_streamednow means two things. On streamed items it means "arrived over MQTT", andupdateAnswerStreamedStateuses it to mean "typewriter finished".hasStreamedActivityrelies on the first meaning. It works today, but a separate flag (_live?) would be harder to break.- The bubble is placed at its first event. A system or event message pushed mid-turn lands below it, and later cards keep growing the bubble above that notice, so the order on screen stops matching the order things happened.
- A tool card with no result yet looks the same as a finished one, since
okundefined isn't treated as failed. Does the agent send a start event before the result? If so, a running state on the card would help.
A stopped turn marks its open items done, so a reasoning block no longer shows Thinking forever. Streamed items carry their own _live flag instead of sharing _streamed with the typewriter. Activity that follows a message pushed mid-turn starts a new bubble below it, and final-answer tool records still upgrade their card wherever it is.
|
Thanks! Done in d7a1553:
|
Steve-Mcl
left a comment
There was a problem hiding this comment.
local testing now good. Approving from an operational perspective - so I am asking that we wait for another tick from one the FE folk before you merge pls.
|
Reviewing now |
cstns
left a comment
There was a problem hiding this comment.
Ran this on the branch itself against the dev Expert through the FlowFuse central broker. Streamed reasoning, tool records and progress messages render in order, the final answer merges in by id, Stop, reload, keyboard access and Insights cards all behave, and the full frontend suite is green. One thing worth fixing inline (the hover on the Input/Output toggles), the rest are small notes.
| </div> | ||
| <!-- Input/output are dev-only today, but render whenever present so a future | ||
| flag flip needs no UI change. --> | ||
| <json-viewer v-if="hasInput" :value="parseJson(item.input)" label="Input" collapsible default-collapsed /> |
There was a problem hiding this comment.
The Input/Output toggles come out as full tertiary buttons here, so hovering them fills the row with the accent colour and fades it in over 0.3s. Inside a card that reads like a bug. Might be worth giving the viewer a plain variant for this spot, or scoping the hover away in this component.
There was a problem hiding this comment.
Done in e8883b7. JsonViewer takes an opt-in toggleKind, and the tool card passes plain, so the toggles no longer fill on hover. Other viewers are unchanged.
| if (typeof value !== 'string') return false | ||
| const trimmed = value.trim() | ||
| if (trimmed === '' || trimmed === AUTOMATION_PLACEHOLDER) return false | ||
| return !trimmed.startsWith('invoke_') |
There was a problem hiding this comment.
On Insights the tool line under the reason comes out as mcp_tool__flowfuse-mcp-gateway-0__invoke_read_tool, since toolTitle is empty and toolName equals toolId. The guard only catches names that start with invoke_. Not a regression, but it defeats the idea of hiding wrappers. Maybe match invoke_ anywhere, or fall back to input.tool_name for gateway calls.
There was a problem hiding this comment.
Done in e8883b7. invoke_ is now rejected after a server prefix too, and a gateway call falls back to input.tool_name.
| this._insertBySeq(target, { ...item, _uuid: uuidv4(), _streamed: false }) | ||
| } | ||
| const { answer: _answer, ...fields } = response | ||
| Object.assign(target, fields) |
There was a problem hiding this comment.
Small robustness one: if a reasoning block is still done: false when the final answer lands, it stays "Thinking" for good and survives a reload. The Expert does send done before the answer, so I couldn't hit it for real, but calling _closeTurnItems(transactionId) here too would make an Expert error path harmless.
There was a problem hiding this comment.
Done in e8883b7. The final answer now marks any open items done as well.
| return typeof this.item.error === 'string' ? this.item.error.trim() : '' | ||
| }, | ||
| // First usable name: friendly title, else the tool's own name, else the raw key. | ||
| title () { |
There was a problem hiding this comment.
The old strip showed per-call and total ms and the records still carry durationMs, but nothing displays it now. Fine if that's on purpose, just checking it wasn't dropped by accident.
There was a problem hiding this comment.
On purpose. In support the time per call isn't useful to the user. Insights is where it might help, so we can add it there if people want it.
| } | ||
|
|
||
| return null | ||
| activity () { |
There was a problem hiding this comment.
Small UX note, not a bug: a paginated turn gave me ten identical "List Hosted Instances" cards with nothing to expand, ten rows where the old strip was one collapsed line. Could be worth folding consecutive identical cards without a reason into one, or leaving it as a follow-up.
There was a problem hiding this comment.
Every tool call now has to carry a reason, in support and Insights alike, so each paged call should get its own card with its reason and something to expand. Cards with nothing to expand would come from a turn on an Expert from before that change, or from an older saved conversation. Could you check which it was? If it still happens on a fresh turn, I'll look into it.
There was a problem hiding this comment.
Try with Expert on Cloud, it should be there @cstns
| </li> | ||
| <li v-if="isWaitingForResponse"> | ||
| <expert-loading-indicator /> | ||
| <expert-loading-indicator :show-messages="!hasStreamedActivity" /> |
There was a problem hiding this comment.
Nit: after Stop, hasStreamedActivity goes false as soon as the in-flight map clears, so the loader comes back with the generic rotating lines under a bubble that was just streaming. Cosmetic, and the stopping indicator predates this PR.
There was a problem hiding this comment.
Done in e8883b7. A turn that is still stopping keeps the loader to its dots.
n-lark
left a comment
There was a problem hiding this comment.
LGTM for my fixes but please address Serban's feedback as well.
JsonViewer takes an opt-in toggleKind, and the tool card uses the plain kind so its Input and Output toggles no longer fill with the accent colour on hover. A gateway wrapper call is named after the tool it invoked. The final answer finishes any item still open, and a stopping turn keeps the loader to its dots.
Closes FlowFuse/engineering#547
Closes FlowFuse/engineering#549
Closes FlowFuse/engineering#551
Closes FlowFuse/engineering#553
Closes FlowFuse/engineering#554
Closes FlowFuse/engineering#556
Part of FlowFuse/engineering#545.
The Expert chat shows a turn's tool calls, reasoning and progress messages as they happen, inside the reply bubble, instead of only a tool-call count with the final answer. Streamed events arrive on the existing
expert:status-messageinflight topic with akindfield (tool-record,reasoningormessage), so no new MQTT topics or ACL changes are needed.Adds
supportsStreamedActivityin the chat context. Expert only streams to clients that send it; older Expert versions ignore it and keep today's behaviour.product-expert.js): each event is placed in the turn's reply bubble inseqorder, an event with a known id updates its item in place, and the final answer merges into the same bubble by id, so nothing is duplicated.TimelineItem.vue: one collapsible card per tool call. The header is the reason for the call on one line, and expanding it shows the full reason, the tool, its input and output.ReasoningBlock.vue: a collapsed "Thinking" / "Thought" block with one line of the reasoning under it, the latest sentence while streaming and the first once done.AgentMessage.vue: progress messages from the Expert as assistant bubbles between the cards.streamedActivity.js: the shared list of streamed and activity kinds.Changes
AiMessage.vuerenders tool records, reasoning and messages in order above the answer. Insights answers and saved conversations use the same tool cards; Insights does not stream.CollapsibleSection.vuegains asummaryslot under the header.Removes
ToolCalls.vueandToolCallItem.vue, the old "N tool calls" strip, replaced by the cards.Tests
seq, updated by id, merged with the final answer, and late updates are ignored.