Editor agent refactored (#76)
重构了editor agent. 使得它能进行多轮工具调用, 把工具调用的结果嵌入到上下文里。 对于上下文的图片, 使用哈希后的id用来引用,不暴露细节信息to llm。 当前实现, 状态直接维护在json doc里, 需要对整个doc加锁,任务目前只能串行。 把SSE改成一个简单请求( 因为生图工具耗时需要二次确认, 不需要再实时展示给用户进度)。 增加确认/取消生图操作。 把tool的参数和错误情况做了反馈。 TODO: 工具调用的规范/prompt还可以改进。 改用external job来做素材生成, 针对来自editor agent 的生成会把生成资产的引用加入到结果json里, 客户端轮询 external job 确定生成状态, 发现结束了或者失败了就 重新get 会话,后端重新提供会话的时候把 结果插入回会话历史里,用来让 ai 引用 以及显示 --------- Co-authored-by: 段舒康 <kdletters@qq.com> Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/76 Reviewed-by: 段舒康 <kdletters@qq.com> Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
This commit was merged in pull request #76.
This commit is contained in:
@@ -598,6 +598,60 @@ describe('apiClient', () => {
|
||||
expect(result).toEqual({ value: 42 });
|
||||
});
|
||||
|
||||
it('reuses the exact post body and request id for an enabled unsafe transport retry', async () => {
|
||||
setStoredAccessToken('editor-agent-token', { emit: false });
|
||||
fetchMock
|
||||
.mockRejectedValueOnce(new TypeError('network unavailable'))
|
||||
.mockResolvedValueOnce(
|
||||
createResponseMock({
|
||||
status: 200,
|
||||
body: JSON.stringify({
|
||||
ok: true,
|
||||
data: { deltaMessages: [] },
|
||||
error: null,
|
||||
meta: { apiVersion: '2026-06-16' },
|
||||
}),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
);
|
||||
const body = JSON.stringify({
|
||||
clientMessageId: 'client-message-1',
|
||||
text: '生成一张图',
|
||||
attachments: [],
|
||||
});
|
||||
|
||||
await requestJson(
|
||||
'/api/editor/agent-conversations/conversation-1/messages',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body,
|
||||
},
|
||||
'发送画布 Agent 消息失败',
|
||||
{
|
||||
authImpact: 'local',
|
||||
retry: {
|
||||
maxRetries: 1,
|
||||
baseDelayMs: 1,
|
||||
maxDelayMs: 1,
|
||||
retryUnsafeMethods: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
const firstInit = fetchMock.mock.calls[0]?.[1] as RequestInit;
|
||||
const secondInit = fetchMock.mock.calls[1]?.[1] as RequestInit;
|
||||
expect(firstInit.body).toBe(body);
|
||||
expect(secondInit.body).toBe(body);
|
||||
expect((firstInit.headers as Record<string, string>)['x-request-id']).toBe(
|
||||
'web-11111111-2222-3333-4444-555555555555',
|
||||
);
|
||||
expect((secondInit.headers as Record<string, string>)['x-request-id']).toBe(
|
||||
(firstInit.headers as Record<string, string>)['x-request-id'],
|
||||
);
|
||||
});
|
||||
|
||||
it('aborts requests when timeoutMs is reached', async () => {
|
||||
setStoredAccessToken('timeout-token', { emit: false });
|
||||
fetchMock.mockImplementation(
|
||||
|
||||
Reference in New Issue
Block a user