This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new aec407743 fix(ai): seed the run id on attach so stop works after a
reconnect (#4613)
aec407743 is described below
commit aec407743a1f894b098f4fe81d3e1246d928d863
Author: Apulupie <[email protected]>
AuthorDate: Mon Sep 21 21:05:34 2026 +0800
fix(ai): seed the run id on attach so stop works after a reconnect (#4613)
`useAgentRun.startStream` cleared `runIdRef.current` before opening the
stream and only learned the run id from the `run_started` event, which
`AgentEventProjector` documents as live-only and the replay timeline has no
counterpart for. A reload that reattached to an in-flight run through
`useActiveRunAttach` therefore never got an id: `stop()` returned early, and
since `Composer` derives the stop button from `isStreaming` alone the button
stayed clickable as a no-op, while `persistRun [...]
`startStream` now takes a `knownRunId` and seeds both the ref and the state
with it. `attach` passes the run id it already holds; `send` keeps its old
behaviour through the default, and `abort` still clears the id.
Fixes #4614
---
web/src/pages/ai/hooks/useAgentRun.test.ts | 22 ++++++++++++++++++++++
web/src/pages/ai/hooks/useAgentRun.ts | 14 ++++++++++----
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/web/src/pages/ai/hooks/useAgentRun.test.ts
b/web/src/pages/ai/hooks/useAgentRun.test.ts
index 207e5e253..768b05197 100644
--- a/web/src/pages/ai/hooks/useAgentRun.test.ts
+++ b/web/src/pages/ai/hooks/useAgentRun.test.ts
@@ -427,6 +427,28 @@ describe('useAgentRun', () => {
expect(result.current.isStreaming).toBe(true);
});
+ it('stopsAnAttachedRunThroughTheApiTest', async () => {
+ const { result } = render();
+
+ // The server replays persisted events on attach; the live vocabulary
starts at run_started,
+ // so no `run_started` frame ever arrives on this stream.
+ await act(async () => {
+ void result.current.attach(7, 41, 12);
+ });
+ await act(async () => {
+ attachedStreams[0].emit(textDelta('resumed'));
+ await flushFrame();
+ });
+ expect(result.current.canStop).toBe(true);
+
+ await act(async () => {
+ await result.current.stop();
+ });
+
+ expect(stopRun).toHaveBeenCalledWith(41);
+ expect(result.current.stopRequested).toBe(true);
+ });
+
it('surfacesAStreamFailureAndStillRefetchesTheTimelineTest', async () => {
const onError = vi.fn();
const refetchTimeline = vi.fn().mockResolvedValue(undefined);
diff --git a/web/src/pages/ai/hooks/useAgentRun.ts
b/web/src/pages/ai/hooks/useAgentRun.ts
index 071c5c17a..63287c61b 100644
--- a/web/src/pages/ai/hooks/useAgentRun.ts
+++ b/web/src/pages/ai/hooks/useAgentRun.ts
@@ -281,6 +281,7 @@ export function useAgentRun(
async (
targetConversationId: number,
open: (handlers: RunStreamHandlers, signal: AbortSignal) =>
Promise<void>,
+ knownRunId: number | null = null,
): Promise<void> => {
// Double-submit guard: Enter twice in one tick must not admit two runs
(the server would
// reject the second with 409 anyway, but the UI should not even try).
@@ -307,8 +308,11 @@ export function useAgentRun(
// screen once this run's answer lands.
setLastRunTokensPerSecond(null);
setLiveTokensPerSecond(null);
- runIdRef.current = null;
- setRunId(null);
+ // `send` learns the id from the live `run_started` frame; an attach
already knows it from
+ // the URL, and the server never replays `run_started` — without seeding
it here the stop
+ // button would render but address nothing until the run finished.
+ runIdRef.current = knownRunId;
+ setRunId(knownRunId);
setLastStatus(null);
setError('');
setStopRequested(false);
@@ -348,8 +352,10 @@ export function useAgentRun(
const attach = useCallback(
(targetConversationId: number, targetRunId: number, after: number):
Promise<void> =>
- startStream(targetConversationId, (handlers, signal) =>
- attachRunStream(targetRunId, after, handlers, signal),
+ startStream(
+ targetConversationId,
+ (handlers, signal) => attachRunStream(targetRunId, after, handlers,
signal),
+ targetRunId,
),
[startStream],
);