Add admin vision lab and LLM vision verification
这个提交包含在:
@@ -66,6 +66,33 @@ async function invokeStructured<T>(params: StructuredParams<T>) {
|
||||
throw lastError instanceof Error ? lastError : new Error("Failed to parse structured LLM response");
|
||||
}
|
||||
|
||||
function contentToPlainText(content: Message["content"]) {
|
||||
if (typeof content === "string") {
|
||||
return content;
|
||||
}
|
||||
|
||||
const parts = Array.isArray(content) ? content : [content];
|
||||
|
||||
return parts
|
||||
.map((part) => {
|
||||
if (typeof part === "string") {
|
||||
return part;
|
||||
}
|
||||
if (part.type === "text") {
|
||||
return part.text;
|
||||
}
|
||||
if (part.type === "image_url") {
|
||||
return `[image] ${part.image_url.url}`;
|
||||
}
|
||||
if (part.type === "file_url") {
|
||||
return `[file] ${part.file_url.url}`;
|
||||
}
|
||||
return "";
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
function parseDataUrl(input: string) {
|
||||
const match = input.match(/^data:(.+?);base64,(.+)$/);
|
||||
if (!match) {
|
||||
@@ -296,7 +323,7 @@ async function createTextCorrectionResult(payload: {
|
||||
|
||||
return {
|
||||
kind: "analysis_corrections" as const,
|
||||
corrections: response.choices[0]?.message?.content || "暂无建议",
|
||||
corrections: contentToPlainText(response.choices[0]?.message?.content || "暂无建议"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -347,16 +374,26 @@ async function runMultimodalCorrectionTask(task: NonNullable<TaskRow>) {
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
const result = {
|
||||
kind: "pose_correction_multimodal" as const,
|
||||
imageUrls: payload.imageUrls,
|
||||
report,
|
||||
corrections: renderMultimodalCorrectionMarkdown(report as Parameters<typeof renderMultimodalCorrectionMarkdown>[0]),
|
||||
visionStatus: "ok" as const,
|
||||
};
|
||||
|
||||
await db.completeVisionTestRun(task.id, {
|
||||
visionStatus: "ok",
|
||||
summary: (report as { summary?: string }).summary ?? null,
|
||||
corrections: result.corrections,
|
||||
report,
|
||||
warning: null,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const fallback = await createTextCorrectionResult(payload);
|
||||
return {
|
||||
const result = {
|
||||
kind: "pose_correction_multimodal" as const,
|
||||
imageUrls: payload.imageUrls,
|
||||
report: null,
|
||||
@@ -364,6 +401,16 @@ async function runMultimodalCorrectionTask(task: NonNullable<TaskRow>) {
|
||||
visionStatus: "fallback" as const,
|
||||
warning: error instanceof Error ? error.message : "Vision model unavailable",
|
||||
};
|
||||
|
||||
await db.completeVisionTestRun(task.id, {
|
||||
visionStatus: "fallback",
|
||||
summary: null,
|
||||
corrections: result.corrections,
|
||||
report: null,
|
||||
warning: result.warning,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户