mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-14 13:10:47 +08:00
feat(qq): stream the full turn feed as discrete messages
Show what the agent is actually doing during a turn, the way Claude Code does: interim explanation text, every Tool call, and the error detail when a tool fails, each as its own pushed message before the final markdown answer. - HarnessReplyTracker.consume now returns every frame of a polling batch in order (same-batch text frames collapse to the latest) instead of dropping all but the last event, which silently ate tool-call frames that shared a batch with their result - tool frames carry callId; status frames carry the correlated toolName and a flattened error text parsed from the tool/result error payload (message, or name: code) - the QQ bridge holds interim step text until the next tool call (or the end of the turn) pushes it, emits 'Tool call <name>' per call, emits 'Tool call <name>\nError: <reason>' for failures, and skips re-sending interim text that already equals the final answer Other channels keep their existing behaviour: the status frame still reads '正在整理结果…' and they ignore the new optional fields.
This commit is contained in:
parent
6171ec0dc1
commit
e1bc9dd0d0
7 changed files with 325 additions and 190 deletions
|
|
@ -432,8 +432,9 @@ test('QQ pushes the final answer without streaming intermediate text frames', as
|
|||
createSession: async () => 'session-new',
|
||||
ensureRunning: async () => true,
|
||||
ask: async (_session, _text, { onUpdate }) => {
|
||||
// 正文流式帧不逐帧推送,避免原地刷新闪烁。
|
||||
await onUpdate({ type: 'text', text: '回答中' });
|
||||
// 正文流式帧不逐帧推送;最终回答与已暂存文本相同时不重复补发。
|
||||
await onUpdate({ type: 'text', text: '最终回' });
|
||||
await onUpdate({ type: 'text', text: '最终回答' });
|
||||
return '最终回答';
|
||||
},
|
||||
},
|
||||
|
|
@ -464,9 +465,9 @@ test('QQ pushes one notice per tool call and ignores status frames', async () =>
|
|||
ask: async (_session, _text, { onUpdate }) => {
|
||||
await onUpdate({ type: 'tool', name: 'bash' });
|
||||
// 工具结束后 Harness 客户端会下发 status 帧“正在整理结果…”。
|
||||
await onUpdate({ type: 'status', text: '正在整理结果…' });
|
||||
await onUpdate({ type: 'status', text: '正在整理结果…', toolName: 'bash' });
|
||||
await onUpdate({ type: 'tool', name: 'read' });
|
||||
await onUpdate({ type: 'status', text: '正在整理结果…' });
|
||||
await onUpdate({ type: 'status', text: '正在整理结果…', toolName: 'read' });
|
||||
return '最终回答';
|
||||
},
|
||||
},
|
||||
|
|
@ -480,7 +481,49 @@ test('QQ pushes one notice per tool call and ignores status frames', async () =>
|
|||
});
|
||||
|
||||
await bridge.accept(message({ messageId: 'msg-status-frame' }));
|
||||
assert.deepEqual(sent, ['正在使用bash…', '正在使用read…', '最终回答']);
|
||||
assert.deepEqual(sent, ['Tool call bash', 'Tool call read', '最终回答']);
|
||||
});
|
||||
|
||||
test('QQ pushes a failed tool error and any interim explanation text', async () => {
|
||||
const sent = [];
|
||||
const bridge = new QqHarnessBridge({
|
||||
bot: {
|
||||
sendText: async (_target, text) => sent.push(text),
|
||||
},
|
||||
ownerUserOpenid: 'owner-openid',
|
||||
harness: {
|
||||
sessionExists: async () => true,
|
||||
ask: async (_session, _text, { onUpdate }) => {
|
||||
await onUpdate({ type: 'text', text: '实体不存在,先创建再添加观察:' });
|
||||
await onUpdate({ type: 'tool', name: 'add_observations' });
|
||||
await onUpdate({
|
||||
type: 'status',
|
||||
text: '正在整理结果…',
|
||||
toolName: 'add_observations',
|
||||
error: 'Error calling add_observations. Status code: 404.',
|
||||
});
|
||||
await onUpdate({ type: 'tool', name: 'create_entities' });
|
||||
await onUpdate({ type: 'status', text: '正在整理结果…', toolName: 'create_entities' });
|
||||
return '已存入两套记忆。';
|
||||
},
|
||||
},
|
||||
state: {
|
||||
hasSeen: () => false,
|
||||
markSeen: async () => {},
|
||||
sessionFor: () => 'session-tool-error',
|
||||
setSession: async () => {},
|
||||
clearSession: async () => {},
|
||||
},
|
||||
});
|
||||
|
||||
await bridge.accept(message({ messageId: 'msg-tool-error' }));
|
||||
assert.deepEqual(sent, [
|
||||
'实体不存在,先创建再添加观察:',
|
||||
'Tool call add_observations',
|
||||
'Tool call add_observations\nError: Error calling add_observations. Status code: 404.',
|
||||
'Tool call create_entities',
|
||||
'已存入两套记忆。',
|
||||
]);
|
||||
});
|
||||
|
||||
test('QQ delivers final group answers as markdown messages', async () => {
|
||||
|
|
@ -579,7 +622,7 @@ test('QQ announces a stopped turn after any tool notices already pushed', async
|
|||
|
||||
await bridge.accept(message({ messageId: 'qq-stopped-stream' }));
|
||||
|
||||
assert.deepEqual(sent, ['正在使用bash…', '已停止。']);
|
||||
assert.deepEqual(sent, ['Tool call bash', '已停止。']);
|
||||
assert.equal(loggedErrors, 0);
|
||||
assert.equal(fixture.seen.has('qq-stopped-stream'), true);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue