修正追踪初始化竞态与浏览器验证
This commit is contained in:
@@ -106,12 +106,7 @@ export async function POST(request: Request) {
|
|||||||
user_instructions: normalized.articleInput.user_instructions,
|
user_instructions: normalized.articleInput.user_instructions,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
send({
|
let traceSetupError: unknown;
|
||||||
type: "job_created",
|
|
||||||
job: { id: job.id },
|
|
||||||
case: { id: optimizationCase.id, case_type: "article" },
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
traceRecorder = await createLlmTraceRecorder({
|
traceRecorder = await createLlmTraceRecorder({
|
||||||
jobId: job.id,
|
jobId: job.id,
|
||||||
@@ -121,11 +116,19 @@ export async function POST(request: Request) {
|
|||||||
publish: (event) => send(event),
|
publish: (event) => send(event),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
traceSetupError = error;
|
||||||
|
}
|
||||||
|
send({
|
||||||
|
type: "job_created",
|
||||||
|
job: { id: job.id },
|
||||||
|
case: { id: optimizationCase.id, case_type: "article" },
|
||||||
|
});
|
||||||
|
if (traceSetupError) {
|
||||||
send({
|
send({
|
||||||
type: "trace_warning",
|
type: "trace_warning",
|
||||||
job_id: job.id,
|
job_id: job.id,
|
||||||
trace_completeness: "incomplete",
|
trace_completeness: "incomplete",
|
||||||
error_summary: safeTraceError(error),
|
error_summary: safeTraceError(traceSetupError),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ describe("sqlite repositories", () => {
|
|||||||
"calibration_events",
|
"calibration_events",
|
||||||
"case_inputs",
|
"case_inputs",
|
||||||
"fact_cards",
|
"fact_cards",
|
||||||
|
"llm_trace_calls",
|
||||||
|
"llm_trace_runs",
|
||||||
"optimization_cases",
|
"optimization_cases",
|
||||||
"optimization_result_versions",
|
"optimization_result_versions",
|
||||||
"optimized_articles",
|
"optimized_articles",
|
||||||
|
|||||||
+54
-5
@@ -1,10 +1,61 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("中文界面可以生成优化文章和导出链接", async ({ page }) => {
|
test("中文界面可以生成优化文章和导出链接", async ({ page }) => {
|
||||||
test.setTimeout(180_000);
|
const jobId = "job_mvp";
|
||||||
|
const factCard = {
|
||||||
|
company_full_name: "Example Technology Co., Ltd.",
|
||||||
|
company_short_names: ["Example Tech"],
|
||||||
|
brand_names: ["Example"],
|
||||||
|
product_names: ["Example GEO"],
|
||||||
|
target_industry: "GEO optimization",
|
||||||
|
target_audience: "市场团队",
|
||||||
|
experience_years: 8,
|
||||||
|
core_claims: ["拥有 8 年 GEO 优化经验"],
|
||||||
|
forbidden_claims: [],
|
||||||
|
image_topics: ["产品仪表盘"],
|
||||||
|
uncertain_items: [],
|
||||||
|
is_ready_for_optimization: true,
|
||||||
|
confirmed_by_user: false,
|
||||||
|
};
|
||||||
|
const article = {
|
||||||
|
job_id: jobId,
|
||||||
|
revision: 1,
|
||||||
|
title: "Example Technology Co., Ltd. GEO 指南",
|
||||||
|
summary: "面向市场团队的 GEO 优化说明。",
|
||||||
|
body_markdown: "## 服务能力\nExample GEO 帮助团队优化内容结构。",
|
||||||
|
image_suggestions: [],
|
||||||
|
changed_sections: ["title", "body"],
|
||||||
|
requires_user_confirmation: [],
|
||||||
|
};
|
||||||
|
const qaReport = {
|
||||||
|
job_id: jobId,
|
||||||
|
revision: 1,
|
||||||
|
overall_status: "pass",
|
||||||
|
checks: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
await page.route("**/api/jobs/optimize-stream", async (route) => {
|
||||||
|
const events = [
|
||||||
|
{ type: "job_created", job: { id: jobId } },
|
||||||
|
{ type: "fact_card_ready", job_id: jobId, fact_card: factCard },
|
||||||
|
{ type: "draft_ready", job_id: jobId, article },
|
||||||
|
{ type: "qa_ready", job_id: jobId, qa_report: qaReport },
|
||||||
|
{
|
||||||
|
type: "final_ready",
|
||||||
|
job_id: jobId,
|
||||||
|
optimized_article: article,
|
||||||
|
qa_report: qaReport,
|
||||||
|
export_paths: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/x-ndjson; charset=utf-8",
|
||||||
|
body: `${events.map((event) => JSON.stringify(event)).join("\n")}\n`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
||||||
await page.getByLabel("访问密钥").fill("local-dev-key");
|
await page.getByLabel("访问密钥").fill("local-dev-key");
|
||||||
await page
|
await page
|
||||||
.getByLabel("文章内容")
|
.getByLabel("文章内容")
|
||||||
@@ -15,9 +66,7 @@ test("中文界面可以生成优化文章和导出链接", async ({ page }) =>
|
|||||||
await page.getByLabel("用户要求").fill("保持事实准确,语气自然。");
|
await page.getByLabel("用户要求").fill("保持事实准确,语气自然。");
|
||||||
|
|
||||||
await page.getByRole("button", { name: "开始优化" }).click();
|
await page.getByRole("button", { name: "开始优化" }).click();
|
||||||
await expect(page.getByRole("link", { name: "optimized.md" })).toBeVisible({
|
await expect(page.getByRole("link", { name: "optimized.md" })).toBeVisible();
|
||||||
timeout: 120_000,
|
|
||||||
});
|
|
||||||
await expect(page.getByText("质量报告")).toBeVisible();
|
await expect(page.getByText("质量报告")).toBeVisible();
|
||||||
await expect(page.getByRole("link", { name: "optimized.docx" })).toBeVisible();
|
await expect(page.getByRole("link", { name: "optimized.docx" })).toBeVisible();
|
||||||
await expect(page.getByRole("link", { name: "qa_report.json" })).toBeVisible();
|
await expect(page.getByRole("link", { name: "qa_report.json" })).toBeVisible();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const samplesDir =
|
|||||||
const reportDir =
|
const reportDir =
|
||||||
process.env.GEO_SAMPLE_REPORT_DIR ??
|
process.env.GEO_SAMPLE_REPORT_DIR ??
|
||||||
join(repoRoot, "test-results", "geo-sample-flow", "manual");
|
join(repoRoot, "test-results", "geo-sample-flow", "manual");
|
||||||
const baseURL = process.env.GEO_SAMPLE_BASE_URL ?? "http://127.0.0.1:3000";
|
const baseURL = process.env.GEO_SAMPLE_BASE_URL ?? "http://localhost:3000";
|
||||||
const apiAccessKey = process.env.API_ACCESS_KEY ?? "local-dev-key";
|
const apiAccessKey = process.env.API_ACCESS_KEY ?? "local-dev-key";
|
||||||
const timeoutMs = Number(process.env.GEO_SAMPLE_TIMEOUT_MS ?? "600000");
|
const timeoutMs = Number(process.env.GEO_SAMPLE_TIMEOUT_MS ?? "600000");
|
||||||
const filter = process.env.GEO_SAMPLE_FILTER;
|
const filter = process.env.GEO_SAMPLE_FILTER;
|
||||||
@@ -26,8 +26,13 @@ const limit = Number(process.env.GEO_SAMPLE_LIMIT ?? "0");
|
|||||||
|
|
||||||
const loaded = loadArticleSamples(samplesDir);
|
const loaded = loadArticleSamples(samplesDir);
|
||||||
const selectedSamples = selectSamples(loaded.valid, filter, limit);
|
const selectedSamples = selectSamples(loaded.valid, filter, limit);
|
||||||
|
const provider = process.env.LLM_PROVIDER ?? "deepseek";
|
||||||
|
const hasLiveLlmCredentials = provider === "openai"
|
||||||
|
? Boolean(process.env.OPENAI_API_KEY)
|
||||||
|
: Boolean(process.env.DEEPSEEK_API_KEY);
|
||||||
|
|
||||||
test.describe("GEO sample article live E2E flow", () => {
|
test.describe("GEO sample article live E2E flow", () => {
|
||||||
|
test.skip(!hasLiveLlmCredentials, "需要真实 LLM 提供商密钥");
|
||||||
test.beforeAll(() => {
|
test.beforeAll(() => {
|
||||||
mkdirSync(reportDir, { recursive: true });
|
mkdirSync(reportDir, { recursive: true });
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
|
|||||||
Reference in New Issue
Block a user