feat: 운영 안정성과 세션 음성 경험 개선
This commit is contained in:
parent
facc4ad2d9
commit
c788343467
95 changed files with 8431 additions and 1785 deletions
|
|
@ -44,6 +44,7 @@ interface AdminEngineConfigResponse {
|
|||
engine_mode: string;
|
||||
engine_url: string;
|
||||
model: string;
|
||||
reasoning_effort: string | null;
|
||||
updated_by: string | null;
|
||||
updated_at: number | null;
|
||||
}
|
||||
|
|
@ -117,11 +118,28 @@ async function waitForReactInputCommit(page: Page) {
|
|||
);
|
||||
}
|
||||
|
||||
function hasEngineConfigRequestBody(engineMode: string, model: string) {
|
||||
interface EngineCapabilitiesResponse {
|
||||
available: boolean;
|
||||
models: Array<{
|
||||
id: string;
|
||||
reasoning_efforts?: string[];
|
||||
default_reasoning_effort?: string | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
function hasEngineConfigRequestBody(engineMode: string, model: string, reasoningEffort: string | null) {
|
||||
return (response: Response) => {
|
||||
try {
|
||||
const body = response.request().postDataJSON() as { engine_mode?: string; model?: string };
|
||||
return body.engine_mode === engineMode && body.model === model;
|
||||
const body = response.request().postDataJSON() as {
|
||||
engine_mode?: string;
|
||||
model?: string;
|
||||
reasoning_effort?: string | null;
|
||||
};
|
||||
return (
|
||||
body.engine_mode === engineMode &&
|
||||
body.model === model &&
|
||||
body.reasoning_effort === reasoningEffort
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -143,7 +161,9 @@ async function expectNoEngineSegmentClipping(page: Page) {
|
|||
height: number;
|
||||
}> = [];
|
||||
|
||||
const segment = document.querySelector<HTMLElement>("#set-engine .vg-set__seg");
|
||||
const segment = document.querySelector<HTMLElement>(
|
||||
"#set-engine [aria-label='AI 엔진 공급자']",
|
||||
);
|
||||
if (!segment) {
|
||||
return [
|
||||
{
|
||||
|
|
@ -666,19 +686,31 @@ test.describe("settings page", () => {
|
|||
|
||||
const engine = page.locator("#set-engine");
|
||||
await expect(engine).toBeVisible();
|
||||
await expect(engine.locator("input").nth(0)).toHaveValue(originalEngineConfig.engine_url);
|
||||
await expect(engine.locator("input").nth(1)).toHaveValue(originalEngineConfig.model);
|
||||
await expect(engine.getByLabel("AI 연결 주소")).toHaveValue(originalEngineConfig.engine_url);
|
||||
await expect(engine.getByLabel("AI 엔진 공급자")).toHaveValue(
|
||||
originalEngineConfig.engine_mode,
|
||||
);
|
||||
await expect(engine.getByLabel("AI 기본 모델")).toHaveValue(originalEngineConfig.model);
|
||||
|
||||
const nextMode =
|
||||
originalEngineConfig.engine_mode === "claude_api" ? "claude_cli" : "claude_api";
|
||||
const nextModel = `e2e-model-${slugFor(testInfo)}`;
|
||||
const capabilityResponse = await page.request.get(
|
||||
`/api/admin/engine-capabilities?engine_mode=${encodeURIComponent(originalEngineConfig.engine_mode)}`,
|
||||
);
|
||||
await expectResponseOk(capabilityResponse);
|
||||
const capability = (await capabilityResponse.json()) as EngineCapabilitiesResponse;
|
||||
const candidate =
|
||||
capability.models.find((model) => model.id !== originalEngineConfig.model) ??
|
||||
capability.models[0];
|
||||
if (!candidate) throw new Error("engine capability returned no selectable model");
|
||||
const nextMode = originalEngineConfig.engine_mode;
|
||||
const nextModel = candidate.id;
|
||||
const nextEffort = candidate.default_reasoning_effort ?? null;
|
||||
|
||||
try {
|
||||
const nextModeButton = engine.locator(`[data-engine-mode="${nextMode}"]`);
|
||||
await nextModeButton.click();
|
||||
await expect(nextModeButton).toHaveAttribute("aria-checked", "true");
|
||||
await engine.locator("input").nth(1).fill(nextModel);
|
||||
await expect(engine.locator("input").nth(1)).toHaveValue(nextModel);
|
||||
await engine.getByLabel("AI 기본 모델").selectOption(nextModel);
|
||||
await expect(engine.getByLabel("AI 기본 모델")).toHaveValue(nextModel);
|
||||
if (nextEffort) {
|
||||
await engine.getByLabel("AI 추론 강도").selectOption(nextEffort);
|
||||
}
|
||||
await waitForReactInputCommit(page);
|
||||
|
||||
const enginePatchResponse = await runAndWaitForApiResponse(
|
||||
|
|
@ -688,16 +720,17 @@ test.describe("settings page", () => {
|
|||
async () => {
|
||||
await engine.locator(".vg-set__foot .vg-btn").click();
|
||||
},
|
||||
hasEngineConfigRequestBody(nextMode, nextModel),
|
||||
hasEngineConfigRequestBody(nextMode, nextModel, nextEffort),
|
||||
);
|
||||
await expectResponseOk(enginePatchResponse);
|
||||
const updatedEngine = (await enginePatchResponse.json()) as AdminEngineConfigResponse;
|
||||
expect(updatedEngine).toMatchObject({
|
||||
engine_mode: nextMode,
|
||||
model: nextModel,
|
||||
reasoning_effort: nextEffort,
|
||||
updated_by: email,
|
||||
});
|
||||
await expect(engine.locator("input").nth(1)).toHaveValue(nextModel);
|
||||
await expect(engine.getByLabel("AI 기본 모델")).toHaveValue(nextModel);
|
||||
|
||||
const healthResponse = await page.request.get("/api/admin/health");
|
||||
await expectResponseOk(healthResponse);
|
||||
|
|
@ -709,6 +742,7 @@ test.describe("settings page", () => {
|
|||
engine_mode: originalEngineConfig.engine_mode,
|
||||
engine_url: originalEngineConfig.engine_url,
|
||||
model: originalEngineConfig.model,
|
||||
reasoning_effort: originalEngineConfig.reasoning_effort,
|
||||
},
|
||||
});
|
||||
await expectResponseOk(restoreResponse);
|
||||
|
|
@ -742,12 +776,13 @@ test.describe("settings page", () => {
|
|||
|
||||
const engine = page.locator("#set-engine");
|
||||
await expect(engine).toBeVisible();
|
||||
await expect(engine.locator("[data-engine-mode]")).toHaveCount(4);
|
||||
await expect(engine.getByLabel("AI 엔진 공급자").locator("option")).toHaveCount(6);
|
||||
await expectNoEngineSegmentClipping(page);
|
||||
await expect(engine.locator("input").nth(0)).toBeVisible();
|
||||
await expect(engine.locator("input").nth(0)).toHaveValue(engineConfig!.engine_url);
|
||||
await expect(engine.locator("input").nth(1)).toBeVisible();
|
||||
await expect(engine.locator("input").nth(1)).toHaveValue(engineConfig!.model);
|
||||
await expect(engine.getByLabel("AI 연결 주소")).toBeVisible();
|
||||
await expect(engine.getByLabel("AI 연결 주소")).toHaveValue(engineConfig!.engine_url);
|
||||
await expect(engine.getByLabel("AI 기본 모델")).toBeVisible();
|
||||
await expect(engine.getByLabel("AI 기본 모델")).toHaveValue(engineConfig!.model);
|
||||
await expect(engine.getByLabel("AI 추론 강도")).toBeVisible();
|
||||
|
||||
await expectNoSettingsControlClipping(page);
|
||||
await expectNoHorizontalOverflow(page);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue