f5368c825f
重构了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>
156 lines
4.4 KiB
TypeScript
156 lines
4.4 KiB
TypeScript
import type {
|
|
CreateEditorAgentConversationRequest,
|
|
EditorAgentConversationDetail,
|
|
EditorAgentConversationListResponse,
|
|
EditorAgentConversationResponse,
|
|
EditorAgentConversationSummary,
|
|
EditorAgentMessageRequest,
|
|
EditorAgentMessageResponse,
|
|
} from '../../../packages/shared/src/contracts/editorAgent';
|
|
import { requestJson } from '../apiClient';
|
|
|
|
const EDITOR_PROJECT_AGENT_CONVERSATION_API_BASE = '/api/editor/projects';
|
|
const EDITOR_AGENT_CONVERSATION_API_BASE = '/api/editor/agent-conversations';
|
|
const EDITOR_AGENT_MESSAGE_TIMEOUT_MS = 1_200_000;
|
|
const EDITOR_AGENT_MESSAGE_RETRY = {
|
|
maxRetries: 1,
|
|
baseDelayMs: 250,
|
|
maxDelayMs: 250,
|
|
retryUnsafeMethods: true,
|
|
} as const;
|
|
|
|
export type SendEditorAgentMessageOptions = {
|
|
signal?: AbortSignal;
|
|
};
|
|
|
|
type DeleteEditorAgentConversationResponse = {
|
|
deletedConversationId: string;
|
|
conversation: EditorAgentConversationSummary;
|
|
};
|
|
|
|
function jsonRequest(method: 'POST', body: Record<string, unknown>) {
|
|
return {
|
|
method,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
};
|
|
}
|
|
|
|
function projectAgentConversationsPath(projectId: string) {
|
|
return `${EDITOR_PROJECT_AGENT_CONVERSATION_API_BASE}/${encodeURIComponent(
|
|
projectId,
|
|
)}/agent-conversations`;
|
|
}
|
|
|
|
function agentConversationPath(conversationId: string) {
|
|
return `${EDITOR_AGENT_CONVERSATION_API_BASE}/${encodeURIComponent(
|
|
conversationId,
|
|
)}`;
|
|
}
|
|
|
|
function agentToolCallActionPath(
|
|
conversationId: string,
|
|
messageId: number,
|
|
action: 'confirm' | 'cancel',
|
|
) {
|
|
return `${agentConversationPath(conversationId)}/messages/${encodeURIComponent(
|
|
String(messageId),
|
|
)}/${action}`;
|
|
}
|
|
|
|
export async function listEditorAgentConversations(
|
|
projectId: string,
|
|
): Promise<EditorAgentConversationSummary[]> {
|
|
const response = await requestJson<EditorAgentConversationListResponse>(
|
|
projectAgentConversationsPath(projectId),
|
|
{ method: 'GET' },
|
|
'读取画布 Agent 会话列表失败',
|
|
);
|
|
return response.conversations;
|
|
}
|
|
|
|
export async function createEditorAgentConversation(
|
|
projectId: string,
|
|
input: CreateEditorAgentConversationRequest = {},
|
|
): Promise<EditorAgentConversationDetail> {
|
|
const body: Record<string, unknown> = {};
|
|
if (input.title !== undefined) {
|
|
body.title = input.title;
|
|
}
|
|
const response = await requestJson<EditorAgentConversationResponse>(
|
|
projectAgentConversationsPath(projectId),
|
|
jsonRequest('POST', body),
|
|
'创建画布 Agent 会话失败',
|
|
);
|
|
return response.conversation;
|
|
}
|
|
|
|
export async function getEditorAgentConversation(
|
|
conversationId: string,
|
|
): Promise<EditorAgentConversationDetail> {
|
|
const response = await requestJson<EditorAgentConversationResponse>(
|
|
agentConversationPath(conversationId),
|
|
{ method: 'GET' },
|
|
'读取画布 Agent 会话失败',
|
|
);
|
|
return response.conversation;
|
|
}
|
|
|
|
export async function deleteEditorAgentConversation(
|
|
conversationId: string,
|
|
): Promise<EditorAgentConversationSummary> {
|
|
const response = await requestJson<DeleteEditorAgentConversationResponse>(
|
|
agentConversationPath(conversationId),
|
|
{ method: 'DELETE' },
|
|
'删除画布 Agent 会话失败',
|
|
);
|
|
return response.conversation;
|
|
}
|
|
|
|
export async function sendEditorAgentMessage(
|
|
conversationId: string,
|
|
payload: EditorAgentMessageRequest,
|
|
options: SendEditorAgentMessageOptions = {},
|
|
): Promise<EditorAgentMessageResponse> {
|
|
return requestJson<EditorAgentMessageResponse>(
|
|
`${agentConversationPath(conversationId)}/messages`,
|
|
{
|
|
...jsonRequest('POST', payload as unknown as Record<string, unknown>),
|
|
signal: options.signal,
|
|
},
|
|
'发送画布 Agent 消息失败',
|
|
{
|
|
timeoutMs: EDITOR_AGENT_MESSAGE_TIMEOUT_MS,
|
|
authImpact: 'local',
|
|
retry: EDITOR_AGENT_MESSAGE_RETRY,
|
|
},
|
|
);
|
|
}
|
|
|
|
export async function confirmEditorAgentToolCall(
|
|
conversationId: string,
|
|
messageId: number,
|
|
): Promise<void> {
|
|
await requestJson<unknown>(
|
|
agentToolCallActionPath(conversationId, messageId, 'confirm'),
|
|
{ method: 'POST' },
|
|
'确认画布 Agent 操作失败',
|
|
{
|
|
timeoutMs: EDITOR_AGENT_MESSAGE_TIMEOUT_MS,
|
|
authImpact: 'local',
|
|
},
|
|
);
|
|
}
|
|
|
|
export async function cancelEditorAgentToolCall(
|
|
conversationId: string,
|
|
messageId: number,
|
|
): Promise<void> {
|
|
await requestJson<unknown>(
|
|
agentToolCallActionPath(conversationId, messageId, 'cancel'),
|
|
{ method: 'POST' },
|
|
'取消画布 Agent 操作失败',
|
|
{ authImpact: 'local' },
|
|
);
|
|
}
|