8dad52f040
新增画布Agent会话契约、领域规则、SpacetimeDB元数据表和生成绑定。 新增api-server画布Agent会话CRUD、OSS消息文档读写、SSE消息流和生成工具编排。 新增前端右侧Agent对话面板、会话状态hook、SSE客户端、BFF客户端和相关测试。 调整画布侧栏、任务栏、小地图默认状态和面板互斥交互。 补齐画布Agent、OSS消息存储、SSE收口、后端契约和项目记忆文档。
133 lines
3.9 KiB
TypeScript
133 lines
3.9 KiB
TypeScript
import type {
|
|
CreateEditorAgentConversationRequest,
|
|
EditorAgentConversationDetail,
|
|
EditorAgentConversationListResponse,
|
|
EditorAgentConversationResponse,
|
|
EditorAgentConversationSummary,
|
|
EditorAgentSseEvent,
|
|
StreamEditorAgentMessageRequest,
|
|
} from '../../../packages/shared/src/contracts/editorAgent';
|
|
import {
|
|
appendApiErrorRequestId,
|
|
parseApiErrorMessage,
|
|
} from '../../../packages/shared/src/http';
|
|
import { fetchWithApiAuth, requestJson } from '../apiClient';
|
|
import { readEditorAgentSseEvents } from './editorAgentSse';
|
|
|
|
const EDITOR_PROJECT_AGENT_CONVERSATION_API_BASE = '/api/editor/projects';
|
|
const EDITOR_AGENT_CONVERSATION_API_BASE = '/api/editor/agent-conversations';
|
|
const EDITOR_AGENT_STREAM_TIMEOUT_MS = 1_200_000;
|
|
|
|
export type StreamEditorAgentMessageOptions = {
|
|
signal?: AbortSignal;
|
|
onEvent?: (event: EditorAgentSseEvent) => void;
|
|
};
|
|
|
|
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,
|
|
)}`;
|
|
}
|
|
|
|
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 streamEditorAgentMessage(
|
|
conversationId: string,
|
|
payload: StreamEditorAgentMessageRequest,
|
|
options: StreamEditorAgentMessageOptions = {},
|
|
) {
|
|
const response = await fetchWithApiAuth(
|
|
`${agentConversationPath(conversationId)}/messages/stream`,
|
|
{
|
|
...jsonRequest('POST', payload as unknown as Record<string, unknown>),
|
|
signal: options.signal,
|
|
},
|
|
{
|
|
timeoutMs: EDITOR_AGENT_STREAM_TIMEOUT_MS,
|
|
authImpact: 'local',
|
|
},
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const responseText = await response.text();
|
|
throw new Error(
|
|
appendApiErrorRequestId(
|
|
parseApiErrorMessage(responseText, '发送画布 Agent 消息失败'),
|
|
response.headers.get('x-request-id'),
|
|
),
|
|
);
|
|
}
|
|
|
|
if (!response.body) {
|
|
throw new Error('streaming response body is unavailable');
|
|
}
|
|
|
|
await readEditorAgentSseEvents(response, (event) => options.onEvent?.(event));
|
|
}
|