Merge codex/sse-stream-architecture into architecture adjustment

This commit is contained in:
2026-06-07 00:23:42 +08:00
136 changed files with 22344 additions and 7543 deletions

View File

@@ -1,4 +1,4 @@
import { beforeEach, expect, test, vi } from 'vitest';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
const requestJsonMock = vi.hoisted(() => vi.fn());
@@ -27,6 +27,10 @@ beforeEach(() => {
requestJsonMock.mockReset();
});
afterEach(() => {
vi.restoreAllMocks();
});
test('wooden fish creation keeps image2 generation requests alive long enough', async () => {
await import('./woodenFishClient');
@@ -51,15 +55,85 @@ test('wooden fish list works uses creation works endpoint', async () => {
);
});
test('wooden fish delete work uses creation works endpoint', async () => {
test('wooden fish start run uses runtime guest json skeleton', async () => {
const { woodenFishClient } = await import('./woodenFishClient');
requestJsonMock.mockResolvedValueOnce({ items: [] });
requestJsonMock.mockResolvedValueOnce({ run: { runId: 'run-1' } });
await woodenFishClient.deleteWork('wooden-fish-profile-1');
await woodenFishClient.startRun('profile/1', {
runtimeGuestToken: 'runtime-guest-token',
});
expect(requestJsonMock).toHaveBeenCalledWith(
'/api/creation/wooden-fish/works/wooden-fish-profile-1',
{ method: 'DELETE' },
'删除敲木鱼作品失败',
'/api/runtime/wooden-fish/runs',
expect.objectContaining({
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer runtime-guest-token',
},
body: JSON.stringify({ profileId: 'profile/1' }),
}),
'启动敲木鱼运行态失败',
expect.objectContaining({
retry: expect.objectContaining({ retryUnsafeMethods: true }),
skipAuth: true,
skipRefresh: true,
}),
);
});
test('wooden fish checkpoint run keeps client event id local to the client', async () => {
const { woodenFishClient } = await import('./woodenFishClient');
vi.spyOn(Date, 'now').mockReturnValue(1780000000000);
requestJsonMock.mockResolvedValueOnce({ run: { runId: 'run-1' } });
await woodenFishClient.checkpointRun(
'run/1',
{
totalTapCount: 12,
wordCounters: [{ text: '功德', count: 3 }],
},
{ runtimeGuestToken: 'runtime-guest-token' },
);
const [, init] = requestJsonMock.mock.calls[0];
const body = JSON.parse(init.body);
expect(requestJsonMock.mock.calls[0][0]).toBe(
'/api/runtime/wooden-fish/runs/run%2F1/checkpoint',
);
expect(body).toEqual({
totalTapCount: 12,
wordCounters: [{ text: '功德', count: 3 }],
clientEventId: 'checkpoint-run/1-1780000000000',
});
expect(body).not.toHaveProperty('runId');
expect(body).not.toHaveProperty('checkpointAtMs');
});
test('wooden fish finish run keeps finish event id local to the client', async () => {
const { woodenFishClient } = await import('./woodenFishClient');
vi.spyOn(Date, 'now').mockReturnValue(1780000000001);
requestJsonMock.mockResolvedValueOnce({ run: { runId: 'run-1' } });
await woodenFishClient.finishRun(
'run/1',
{
totalTapCount: 18,
wordCounters: [{ text: '清净', count: 2 }],
},
{ runtimeGuestToken: 'runtime-guest-token' },
);
const [, init] = requestJsonMock.mock.calls[0];
const body = JSON.parse(init.body);
expect(requestJsonMock.mock.calls[0][0]).toBe(
'/api/runtime/wooden-fish/runs/run%2F1/finish',
);
expect(body).toEqual({
totalTapCount: 18,
wordCounters: [{ text: '清净', count: 2 }],
clientEventId: 'finish-run/1-1780000000001',
});
expect(body).not.toHaveProperty('runId');
expect(body).not.toHaveProperty('finishedAtMs');
});

View File

@@ -13,17 +13,14 @@ import type {
WoodenFishWorkDetailResponse,
WoodenFishWorkMutationResponse,
WoodenFishWorkProfileResponse,
WoodenFishWorksResponse,
WoodenFishWorkspaceCreateRequest,
WoodenFishWorksResponse,
WoodenFishWorkSummaryResponse,
} from '../../../packages/shared/src/contracts/woodenFish';
import { type ApiRetryOptions, requestJson } from '../apiClient';
import { createCreationAgentClient } from '../creation-agent';
import {
buildRuntimeGuestAuthOptions,
buildRuntimeGuestHeaders,
type RuntimeGuestRequestOptions,
} from '../runtimeGuestAuth';
import { type RuntimeGuestRequestOptions } from '../runtimeGuestAuth';
import { buildRuntimeApiPath, requestRuntimeJson } from '../runtimeRequest';
const WOODEN_FISH_API_BASE = '/api/creation/wooden-fish/sessions';
const WOODEN_FISH_WORKS_API_BASE = '/api/creation/wooden-fish/works';
@@ -58,8 +55,8 @@ export type {
WoodenFishWorkDetailResponse,
WoodenFishWorkMutationResponse,
WoodenFishWorkProfileResponse,
WoodenFishWorksResponse,
WoodenFishWorkspaceCreateRequest,
WoodenFishWorksResponse,
};
export type CreateWoodenFishSessionRequest = WoodenFishWorkspaceCreateRequest;
export type WoodenFishSessionSnapshot = WoodenFishSessionSnapshotResponse;
@@ -245,23 +242,14 @@ export async function startWoodenFishRuntimeRun(
profileId: string,
options: WoodenFishRuntimeRequestOptions = {},
) {
const requestOptions = buildRuntimeGuestAuthOptions(options);
return requestJson<WoodenFishRunResponse>(
`${WOODEN_FISH_RUNTIME_API_BASE}/runs`,
{
method: 'POST',
headers: {
'content-type': 'application/json',
...buildRuntimeGuestHeaders(options),
},
body: JSON.stringify({ profileId }),
},
'启动敲木鱼运行态失败',
{
retry: WOODEN_FISH_RUNTIME_WRITE_RETRY,
...requestOptions,
},
);
return requestRuntimeJson<WoodenFishRunResponse>({
url: buildRuntimeApiPath(WOODEN_FISH_RUNTIME_API_BASE, 'runs'),
method: 'POST',
jsonBody: { profileId },
fallbackMessage: '启动敲木鱼运行态失败',
retry: WOODEN_FISH_RUNTIME_WRITE_RETRY,
requestOptions: options,
});
}
export async function checkpointWoodenFishRun(
@@ -269,28 +257,24 @@ export async function checkpointWoodenFishRun(
payload: Omit<WoodenFishCheckpointRunRequest, 'clientEventId'>,
options: WoodenFishRuntimeRequestOptions = {},
) {
const requestOptions = buildRuntimeGuestAuthOptions(options);
const requestPayload: WoodenFishCheckpointRunRequest = {
...payload,
clientEventId: `checkpoint-${runId}-${Date.now()}`,
};
return requestJson<WoodenFishRunResponse>(
`${WOODEN_FISH_RUNTIME_API_BASE}/runs/${encodeURIComponent(runId)}/checkpoint`,
{
method: 'POST',
headers: {
'content-type': 'application/json',
...buildRuntimeGuestHeaders(options),
},
body: JSON.stringify(requestPayload),
},
'保存敲木鱼进度失败',
{
retry: WOODEN_FISH_RUNTIME_WRITE_RETRY,
...requestOptions,
},
);
return requestRuntimeJson<WoodenFishRunResponse>({
url: buildRuntimeApiPath(
WOODEN_FISH_RUNTIME_API_BASE,
'runs',
runId,
'checkpoint',
),
method: 'POST',
jsonBody: requestPayload,
fallbackMessage: '保存敲木鱼进度失败',
retry: WOODEN_FISH_RUNTIME_WRITE_RETRY,
requestOptions: options,
});
}
export async function finishWoodenFishRun(
@@ -298,28 +282,24 @@ export async function finishWoodenFishRun(
payload: Omit<WoodenFishFinishRunRequest, 'clientEventId'>,
options: WoodenFishRuntimeRequestOptions = {},
) {
const requestOptions = buildRuntimeGuestAuthOptions(options);
const requestPayload: WoodenFishFinishRunRequest = {
...payload,
clientEventId: `finish-${runId}-${Date.now()}`,
};
return requestJson<WoodenFishRunResponse>(
`${WOODEN_FISH_RUNTIME_API_BASE}/runs/${encodeURIComponent(runId)}/finish`,
{
method: 'POST',
headers: {
'content-type': 'application/json',
...buildRuntimeGuestHeaders(options),
},
body: JSON.stringify(requestPayload),
},
'结束敲木鱼运行失败',
{
retry: WOODEN_FISH_RUNTIME_WRITE_RETRY,
...requestOptions,
},
);
return requestRuntimeJson<WoodenFishRunResponse>({
url: buildRuntimeApiPath(
WOODEN_FISH_RUNTIME_API_BASE,
'runs',
runId,
'finish',
),
method: 'POST',
jsonBody: requestPayload,
fallbackMessage: '结束敲木鱼运行失败',
retry: WOODEN_FISH_RUNTIME_WRITE_RETRY,
requestOptions: options,
});
}
export const woodenFishClient = {