Initial commit: OmniAI Web Frontend
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { buildApiUrl, buildAuthHeaders } from "./serverConnection";
|
||||
|
||||
export interface ScriptEvalResult {
|
||||
totalScore: number;
|
||||
grade: string;
|
||||
dimensionScores: Record<string, number>;
|
||||
summary: string;
|
||||
issues: string[];
|
||||
highlights: string[];
|
||||
suggestions: string[];
|
||||
}
|
||||
|
||||
const EVAL_SYSTEM_PROMPT = `你是一位专业的剧本评测专家。请对用户提供的剧本进行六维评分分析,并以严格的 JSON 格式返回结果。
|
||||
|
||||
六个评分维度:
|
||||
1. hook(钩子设计,满分20):开篇吸引力、悬念设置、黄金三秒法则
|
||||
2. character(角色塑造,满分15):人物立体度、动机合理性、弧光设计
|
||||
3. plot(剧情结构,满分20):起承转合、节奏把控、冲突设计
|
||||
4. dialogue(台词对白,满分15):语言质感、角色差异化、潜台词
|
||||
5. visual(画面表现,满分15):镜头感、空间层次、视觉冲击力
|
||||
6. content(内容深度,满分15):主题表达、情感共鸣、思想内核
|
||||
|
||||
请严格按以下 JSON 格式返回(不要包含任何其他文字):
|
||||
{
|
||||
"dimensionScores": { "hook": 数字, "character": 数字, "plot": 数字, "dialogue": 数字, "visual": 数字, "content": 数字 },
|
||||
"summary": "一句话总结评价",
|
||||
"issues": ["问题1", "问题2", ...],
|
||||
"highlights": ["亮点1", "亮点2", ...],
|
||||
"suggestions": ["建议1", "建议2", ...]
|
||||
}`;
|
||||
|
||||
const DIMENSION_WEIGHTS: Record<string, { maxScore: number; weight: number }> = {
|
||||
hook: { maxScore: 20, weight: 0.2 },
|
||||
character: { maxScore: 15, weight: 0.15 },
|
||||
plot: { maxScore: 20, weight: 0.2 },
|
||||
dialogue: { maxScore: 15, weight: 0.15 },
|
||||
visual: { maxScore: 15, weight: 0.15 },
|
||||
content: { maxScore: 15, weight: 0.15 },
|
||||
};
|
||||
|
||||
function computeTotalAndGrade(scores: Record<string, number>): { totalScore: number; grade: string } {
|
||||
const totalScore = Math.round(
|
||||
Object.entries(DIMENSION_WEIGHTS).reduce((sum, [key, dim]) => {
|
||||
const score = Math.max(0, Math.min(dim.maxScore, scores[key] ?? 0));
|
||||
return sum + (score / dim.maxScore) * 100 * dim.weight;
|
||||
}, 0),
|
||||
);
|
||||
const grade = totalScore >= 90 ? "S" : totalScore >= 80 ? "A" : totalScore >= 70 ? "B" : totalScore >= 60 ? "C" : "D";
|
||||
return { totalScore, grade };
|
||||
}
|
||||
|
||||
function extractJson(text: string): unknown {
|
||||
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
||||
const raw = fenced ? fenced[1].trim() : text.trim();
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
|
||||
export async function evaluateScript(script: string, signal?: AbortSignal): Promise<ScriptEvalResult> {
|
||||
const res = await fetch(buildApiUrl("ai/chat"), {
|
||||
method: "POST",
|
||||
headers: buildAuthHeaders(),
|
||||
body: JSON.stringify({
|
||||
model: "qwen3.7-max",
|
||||
messages: [
|
||||
{ role: "system", content: EVAL_SYSTEM_PROMPT },
|
||||
{ role: "user", content: `请评测以下剧本:\n\n${script.slice(0, 8000)}` },
|
||||
],
|
||||
stream: false,
|
||||
temperature: 0.3,
|
||||
}),
|
||||
signal,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`评测请求失败 (${res.status})`);
|
||||
}
|
||||
|
||||
const payload = await res.json();
|
||||
const content: string = payload?.choices?.[0]?.message?.content
|
||||
?? payload?.result?.content
|
||||
?? payload?.content
|
||||
?? payload?.text
|
||||
?? (typeof payload === "string" ? payload : "");
|
||||
|
||||
if (!content) throw new Error("模型未返回有效内容");
|
||||
|
||||
const parsed = extractJson(content) as Record<string, unknown>;
|
||||
const dimensionScores: Record<string, number> = {};
|
||||
const rawScores = parsed.dimensionScores as Record<string, number> | undefined;
|
||||
if (!rawScores || typeof rawScores !== "object") throw new Error("评分格式异常");
|
||||
|
||||
for (const key of Object.keys(DIMENSION_WEIGHTS)) {
|
||||
const val = Number(rawScores[key] ?? 0);
|
||||
dimensionScores[key] = Math.max(0, Math.min(DIMENSION_WEIGHTS[key].maxScore, val));
|
||||
}
|
||||
|
||||
const { totalScore, grade } = computeTotalAndGrade(dimensionScores);
|
||||
|
||||
return {
|
||||
totalScore,
|
||||
grade,
|
||||
dimensionScores,
|
||||
summary: String(parsed.summary || ""),
|
||||
issues: Array.isArray(parsed.issues) ? parsed.issues.map(String) : [],
|
||||
highlights: Array.isArray(parsed.highlights) ? parsed.highlights.map(String) : [],
|
||||
suggestions: Array.isArray(parsed.suggestions) ? parsed.suggestions.map(String) : [],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user