1
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user