This commit is contained in:
2026-04-21 18:27:46 +08:00
parent 04bff9617d
commit 4372ab5be1
358 changed files with 30788 additions and 14737 deletions

View File

@@ -1,33 +1,208 @@
import type {
CreateRpgAgentSessionRequest,
CreateRpgAgentSessionResponse,
GetRpgAgentCardDetailResponse,
RpgAgentDraftCardDetail,
RpgAgentOperationRecord,
RpgAgentSessionSnapshot,
SendRpgAgentMessageRequest,
} from '../../../packages/shared/src';
import type { RpgAgentActionRequest } from '../../../packages/shared/src';
import type { TextStreamOptions } from '../aiTypes';
import {
createCustomWorldAgentSession,
executeCustomWorldAgentAction,
getCustomWorldAgentCardDetail,
getCustomWorldAgentOperation,
getCustomWorldAgentSession,
sendCustomWorldAgentMessage,
streamCustomWorldAgentMessage,
} from '../aiService';
openRpgCreationSsePost,
requestRpgCreationPostJson,
} from './rpgCreationRequestHelpers';
import { requestRpgCreationRuntimeJson } from './rpgCreationRuntimeClient';
const RPG_AGENT_API_BASE = '/custom-world/agent/sessions';
export async function createRpgCreationSession(
payload: CreateRpgAgentSessionRequest,
) {
return requestRpgCreationRuntimeJson<CreateRpgAgentSessionResponse>(
RPG_AGENT_API_BASE,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
'创建世界共创会话失败',
);
}
export async function getRpgCreationSession(sessionId: string) {
return requestRpgCreationRuntimeJson<RpgAgentSessionSnapshot>(
`${RPG_AGENT_API_BASE}/${encodeURIComponent(sessionId)}`,
{
method: 'GET',
},
'读取世界共创会话失败',
);
}
export async function sendRpgCreationMessage(
sessionId: string,
payload: SendRpgAgentMessageRequest,
) {
return requestRpgCreationPostJson<{ operation: RpgAgentOperationRecord }>(
`/api/runtime${RPG_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/messages`,
payload,
'发送共创消息失败',
);
}
export async function streamRpgCreationMessage(
sessionId: string,
payload: SendRpgAgentMessageRequest,
options: TextStreamOptions = {},
) {
const response = await openRpgCreationSsePost(
`/api/runtime${RPG_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/messages/stream`,
payload,
'发送共创消息失败',
);
const streamBody = response.body;
if (!streamBody) {
throw new Error('streaming response body is unavailable');
}
const reader = streamBody.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let finalSession: RpgAgentSessionSnapshot | null = null;
for (;;) {
const { done, value } = await reader.read();
if (done) {
break;
}
buffer += decoder.decode(value, { stream: true });
while (buffer.includes('\n\n')) {
const boundary = buffer.indexOf('\n\n');
const eventBlock = buffer.slice(0, boundary);
buffer = buffer.slice(boundary + 2);
let eventName = 'message';
const dataLines: string[] = [];
for (const rawLine of eventBlock.split(/\r?\n/u)) {
const line = rawLine.trim();
if (line.startsWith('event:')) {
eventName = line.slice(6).trim() || 'message';
continue;
}
if (line.startsWith('data:')) {
dataLines.push(line.slice(5).trim());
}
}
if (dataLines.length === 0) {
continue;
}
const data = dataLines.join('\n');
let parsed: Record<string, unknown> | null = null;
try {
parsed = JSON.parse(data) as Record<string, unknown>;
} catch {
parsed = null;
}
if (eventName === 'reply_delta' && parsed) {
const text = parsed.text;
if (typeof text === 'string') {
options.onUpdate?.(text);
}
continue;
}
if (eventName === 'session' && parsed?.session) {
finalSession = parsed.session as RpgAgentSessionSnapshot;
continue;
}
if (eventName === 'error' && parsed) {
const message =
typeof parsed.message === 'string' && parsed.message.trim()
? parsed.message.trim()
: '发送共创消息失败';
throw new Error(message);
}
}
}
if (!finalSession) {
throw new Error('共创消息流式结果不完整');
}
return finalSession;
}
export async function executeRpgCreationAction(
sessionId: string,
payload: RpgAgentActionRequest,
) {
return requestRpgCreationRuntimeJson<{ operation: RpgAgentOperationRecord }>(
`${RPG_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/actions`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
'执行共创操作失败',
);
}
export async function getRpgCreationOperation(
sessionId: string,
operationId: string,
): Promise<RpgAgentOperationRecord> {
const response = await requestRpgCreationRuntimeJson<
{
operation?: RpgAgentOperationRecord;
data?: RpgAgentOperationRecord;
} & Partial<RpgAgentOperationRecord>
>(
`${RPG_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/operations/${encodeURIComponent(operationId)}`,
{
method: 'GET',
},
'读取共创操作状态失败',
);
return (response.operation ?? response.data ?? response) as RpgAgentOperationRecord;
}
export async function getRpgCreationCardDetail(
sessionId: string,
cardId: string,
) {
const response = await requestRpgCreationRuntimeJson<GetRpgAgentCardDetailResponse>(
`${RPG_AGENT_API_BASE}/${encodeURIComponent(sessionId)}/cards/${encodeURIComponent(cardId)}`,
{
method: 'GET',
},
'读取草稿卡详情失败',
);
return response.card as RpgAgentDraftCardDetail;
}
/**
* 工作包 A 先把 RPG 创作 Agent 请求聚合到独立 client 命名空间。
* 当前仍直接透传旧实现,后续工作包 D 再逐步把 `aiService.ts` 中的相关接口迁出
* 工作包 D 开始让 RPG 创作 Agent client 持有真实请求实现,
* 旧 `aiService.ts` 仅保留兼容导出,避免主链请求继续回流到通用服务文件
*/
export const rpgCreationAgentClient = {
createSession: createCustomWorldAgentSession,
getSession: getCustomWorldAgentSession,
sendMessage: sendCustomWorldAgentMessage,
streamMessage: streamCustomWorldAgentMessage,
executeAction: executeCustomWorldAgentAction,
getOperation: getCustomWorldAgentOperation,
getCardDetail: getCustomWorldAgentCardDetail,
};
export {
createCustomWorldAgentSession as createRpgCreationSession,
executeCustomWorldAgentAction as executeRpgCreationAction,
getCustomWorldAgentCardDetail as getRpgCreationCardDetail,
getCustomWorldAgentOperation as getRpgCreationOperation,
getCustomWorldAgentSession as getRpgCreationSession,
sendCustomWorldAgentMessage as sendRpgCreationMessage,
streamCustomWorldAgentMessage as streamRpgCreationMessage,
createSession: createRpgCreationSession,
getSession: getRpgCreationSession,
sendMessage: sendRpgCreationMessage,
streamMessage: streamRpgCreationMessage,
executeAction: executeRpgCreationAction,
getOperation: getRpgCreationOperation,
getCardDetail: getRpgCreationCardDetail,
};