This commit is contained in:
2026-04-22 20:14:15 +08:00
parent 0773a0d0ca
commit 0e9c286a57
205 changed files with 25790 additions and 1623 deletions

View File

@@ -0,0 +1,168 @@
import type {
PuzzleAgentActionRequest,
PuzzleAgentActionResponse,
} from '../../../packages/shared/src/contracts/puzzleAgentActions';
import type {
CreatePuzzleAgentSessionRequest,
CreatePuzzleAgentSessionResponse,
PuzzleAgentSessionSnapshot,
SendPuzzleAgentMessageRequest,
} from '../../../packages/shared/src/contracts/puzzleAgentSession';
import { parseApiErrorMessage } from '../../../packages/shared/src/http';
import type { TextStreamOptions } from '../aiTypes';
import {
type ApiRetryOptions,
fetchWithApiAuth,
requestJson,
} from '../apiClient';
import { readCreationAgentSessionFromSse } from '../creation-agent';
const PUZZLE_AGENT_API_BASE = '/api/runtime/puzzle/agent/sessions';
const PUZZLE_AGENT_READ_RETRY: ApiRetryOptions = {
maxRetries: 1,
baseDelayMs: 180,
maxDelayMs: 480,
};
const PUZZLE_AGENT_WRITE_RETRY: ApiRetryOptions = {
maxRetries: 1,
baseDelayMs: 240,
maxDelayMs: 640,
retryUnsafeMethods: true,
};
/**
* 创建拼图 Agent 共创会话。
* 首版继续走 Axum facade前端不直连 SpacetimeDB。
*/
export async function createPuzzleAgentSession(
payload: CreatePuzzleAgentSessionRequest = {},
) {
return requestJson<CreatePuzzleAgentSessionResponse>(
PUZZLE_AGENT_API_BASE,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
'创建拼图共创会话失败',
{
retry: PUZZLE_AGENT_WRITE_RETRY,
},
);
}
/**
* 读取拼图 Agent 会话快照。
*/
export async function getPuzzleAgentSession(sessionId: string) {
return requestJson<CreatePuzzleAgentSessionResponse>(
`${PUZZLE_AGENT_API_BASE}/${encodeURIComponent(sessionId)}`,
{
method: 'GET',
},
'读取拼图共创会话失败',
{
retry: PUZZLE_AGENT_READ_RETRY,
},
);
}
/**
* 非流式发送拼图 Agent 消息。
* 当前 UI 主链使用 SSE但保留普通接口便于后续降级。
*/
export async function sendPuzzleAgentMessage(
sessionId: string,
payload: SendPuzzleAgentMessageRequest,
) {
return requestJson<{ session: PuzzleAgentSessionSnapshot }>(
`${PUZZLE_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/messages`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
'发送拼图共创消息失败',
{
retry: PUZZLE_AGENT_WRITE_RETRY,
},
);
}
/**
* 流式发送拼图 Agent 消息。
* 后端当前会先回传一段 assistant 文本,再附上最新 session 快照。
*/
export async function streamPuzzleAgentMessage(
sessionId: string,
payload: SendPuzzleAgentMessageRequest,
options: TextStreamOptions = {},
) {
const response = await openPuzzleAgentSsePost(
`${PUZZLE_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/messages/stream`,
payload,
'发送拼图共创消息失败',
);
return readCreationAgentSessionFromSse<PuzzleAgentSessionSnapshot>(
response,
{
...options,
fallbackMessage: '发送拼图共创消息失败',
incompleteMessage: '拼图共创消息流式结果不完整',
},
);
}
/**
* 执行拼图结果页相关操作。
* 后端会返回 operation 记录,前端再按需刷新 session 或 works/gallery。
*/
export async function executePuzzleAgentAction(
sessionId: string,
payload: PuzzleAgentActionRequest,
) {
return requestJson<PuzzleAgentActionResponse>(
`${PUZZLE_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/actions`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
'执行拼图共创操作失败',
{
retry: PUZZLE_AGENT_WRITE_RETRY,
},
);
}
async function openPuzzleAgentSsePost(
url: string,
payload: unknown,
fallbackMessage: string,
) {
const response = await fetchWithApiAuth(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
const responseText = await response.text();
throw new Error(parseApiErrorMessage(responseText, fallbackMessage));
}
if (!response.body) {
throw new Error('streaming response body is unavailable');
}
return response;
}
export const puzzleAgentClient = {
createSession: createPuzzleAgentSession,
getSession: getPuzzleAgentSession,
sendMessage: sendPuzzleAgentMessage,
streamMessage: streamPuzzleAgentMessage,
executeAction: executePuzzleAgentAction,
};